1. Rights management

1.1 What Is Rights Management?

Permission management Allows users to access only authorized resources based on security rules or policies

Permission management includes user identity authentication and authorization, which are referred to as authentication and authorization

1.2 What is Identity Authentication?

Identity authentication is the process of checking whether a user is a valid user. The most common method is to check whether the user’s input and user name and password are consistent with those stored in the system

1.3 What is Authorization?

Authorization, namely, access control, controls who can access which resources. After identity authentication, subjects need to assign permissions to access system resources. Some resources cannot be accessed without permissions

2. What is Shiro?

Shiro is a powerful and easy-to-use Java security framework capable of authentication, authorization, encryption, and session management

2.1 Shiro’s core architecture

  • Subject

    Subject, the external application interacts with the Subject, who records the current operating user and understands the concept of user as the subject of the current operation, either a user requesting through a browser or a running program. Subject is an interface in Shiro, and many authentication methods are defined in the interface. External programs are authenticated by Subject, while Subject is authenticated and authorized by SecurityManager

  • SecurityManager

    The security manager, which manages security for all subjects, is at the heart of Shiro. You can use the SecurityManager to authenticate and authorize a subject. Essentially, the SecurityManager authenticates a subject using an Authenticator and Authorizer. Session management is performed through the SessionManager. SecurityManager is an interface that inherits the Authenticator, Authorizer, and SessionManager interfaces

  • Authenticator

    Authentication, the user identity authentication, the Authenticator is an interface, shiro provides ModularRealmAuthenticator implementation class, through ModularRealmAuthenticator basically can satisfy most requirements, You can also customize the authenticator

  • Authorizer

    Authenticator: The user is authenticated by the authenticator. When accessing the function, the user needs to check whether the user has the operation permission for the function

  • Realm

    Realm, which is the equivalent of a datasource datasource, requires the securityManager to obtain user permission data from the Realm for security authentication. For example, if user identity data is in the database, then the Realm needs to obtain user identity information from the database

  • SessionManager

    Session management, Shiro framework defines a set of session management, it does not rely on the Web container session, so Shiro can be used in non-Web applications, distributed applications can be centralized session management, this feature enables it to achieve single sign-on

  • SessionDAO

    Session DAO is a set of interfaces for session operations. For example, to store a session to a database, you can use JDBC to store a session to a database

  • CacheManager

    CacheManager stores user permission data in the cache to improve performance

  • Cryptography

    Password management, Shiro provides a set of encryption and decryption components for easy development, such as providing common hashing, encryption and decryption functions

3. Authentication in Shiro

3.1 certified

Identity authentication is the process of determining whether a user is a legitimate user. The most common authentication method is that the system checks whether the user name and password entered by the user are consistent with the user name and password stored in the system to determine whether the user identity is correct

3.2 Key objects in ShirO Authentication

  • Subject: the Subject

    Users who access the system, subjects can be users, programs, etc., and those who authenticate are called subjects

  • Principal: Identity information

    It is the identification of the subject for identity authentication, which must be unique, such as user name, mobile phone number, email address, etc. A subject can have multiple identities, but it must have a Primary Principal identity.

  • B: Credential information

    Is security information known only to the subject, such as passwords, certificates, etc

3.3 Authentication Process

3.4 Authentication Development

3.4.1 Introducing dependencies

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.5.3</version>
</dependency>
Copy the code

3.4.2 Creating a Configuration File

The configuration file is used to write permission data in the system for learning

[users]
xiaochen=123
zhangsan=456
Copy the code

3.4.3 Developing authentication codes

public class TestAuthenticator {

    public static void main(String[] args) {
        // 1. Create a security manager object
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        // 2. Set the realm for the security manager
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
        // 3. Set the security manager for the SecurityUtils global security tool class
        SecurityUtils.setSecurityManager(securityManager);
        // 4. Obtain the authentication body
        Subject subject = SecurityUtils.getSubject();
        // 5. Create token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen"."123");
        / / 6. Certification
        try {
            System.out.println("Certification Status:" + subject.isAuthenticated());
            subject.login(token);
            System.out.println("Certification Status:" + subject.isAuthenticated());
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

3.5 Customize the Realm

A custom Realm implementation is an implementation that transforms the authentication/authorization data source into a database

public class TestCustomerRealmAuthenticator {

    public static void main(String[] args) {
        // 1. Create a security manager object
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        // 2. Set custom realms
        securityManager.setRealm(new CustomerRealm());
        // 3. Set the security manager for the SecurityUtils global security tool class
        SecurityUtils.setSecurityManager(securityManager);
        // 4. Obtain the authentication body
        Subject subject = SecurityUtils.getSubject();
        // 5. Create token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen"."123");
        / / 6. Certification
        try {
            System.out.println("Certification Status:" + subject.isAuthenticated());
            subject.login(token);
            System.out.println("Certification Status:" + subject.isAuthenticated());
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

Custom Realm code implementation

public class CustomerRealm extends AuthorizingRealm {

    / / authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    / / certification
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 1. Obtain the user name from the token
        String principal = (String) authenticationToken.getPrincipal();
        // 2. Query database data
        if ("xiaochen".equals(principal)) {
            // Parameter 1: the correct user name
            // Parameter 2: the correct password
            // Argument 3: Provides the name of the current realm
            SimpleAuthenticationInfo simpleAuthenticationInfo
                    = new SimpleAuthenticationInfo("xianchen"."123".this.getName());
            return simpleAuthenticationInfo;
        }
        return null; }}Copy the code

3.6 Plaintext Encryption

In practice, it is impossible to display the user password in plain text, so it needs to be encrypted

The usual encryption method is md5 + salt + hash. The verification process is as follows: save the salt and hashed value, and complete the password verification in Shiro

The following is an example of code to do this using the API provided by Shiro

public class TestShiroMD5 {

    public static void main(String[] args) {
        // 1. Use MD5 encryption
        // Parameter 1 is the plaintext password
        Md5Hash md5Hash1 = new Md5Hash("123");
        // Prints the encrypted ciphertext
        / / the result: 202 cb962ac59075b964b07152d234b70
        System.out.println(md5Hash1.toHex());
        // 2. Use MD5 +salt encryption
        Md5Hash md5Hash2 = new Md5Hash("123"."X0*7ps");
        // 8a83592a02263bfe6752b2b5b03a4799
        System.out.println(md5Hash2.toHex());
        // 3. Use MD5 +salt+hash for encryption
        Md5Hash md5Hash3 = new Md5Hash("123"."X0*7ps".1024);
        // e4f9bf3e0c58f045e62c23c533fcf633System.out.println(md5Hash3.toHex()); }}Copy the code

Custom CustomerMd5Realm

public class CustomerMd5Realm extends AuthorizingRealm {

    / / authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    / / certification
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 1. Obtain the user name from the token
        String principal = (String) authenticationToken.getPrincipal();
        // 2. Query database data
        if ("xiaochen".equals(principal)) {
            // Parameter 1: the correct user name
            // Parameter 2: the correct password
            // Argument 3: Provides the name of the current realm
            // md5
            // return new SimpleAuthenticationInfo(principal, "202cb962ac59075b964b07152d234b70", this.getName());
            // md5+salt
            /*return new SimpleAuthenticationInfo(principal, "8a83592a02263bfe6752b2b5b03a4799", ByteSource.Util.bytes("X0*7ps"), this.getName()); * /
        }
        return null; }}Copy the code

Check the process

public class TestCustomerMd5RealmAuthenticator {

    public static void main(String[] args) {
        // 1. Create a security manager object
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        // 2. Set custom realms
        CustomerMd5Realm realm = new CustomerMd5Realm();
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // Use MD5 encryption
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // Hash times
        hashedCredentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        securityManager.setRealm(realm);
        // 3. Set the security manager for the SecurityUtils global security tool class
        SecurityUtils.setSecurityManager(securityManager);
        // 4. Obtain the authentication body
        Subject subject = SecurityUtils.getSubject();
        // 5. Create token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen"."123");
        / / 6. Certification
        try {
            System.out.println("Certification Status:" + subject.isAuthenticated());
            subject.login(token);
            System.out.println("Certification Status:" + subject.isAuthenticated());
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

4. Authorization in Shiro

4.1 license

Authorization, or access control, controls who can access what resources. After identity authentication, the principal needs to assign permissions to access system resources. Some resources without permissions cannot be accessed

4.2 Key Objects

Authorization can be simply understood as who does How to what(which) :

  • Who, or Subject, needs to access resources in the system
  • What refers to resources, such as system menus, pages, buttons, class methods, system commodity information, etc. Resources include resource types and resource instances. For example, if the commodity information is resource type, the commodity whose type is T01 is resource instance, and the commodity whose id is 001 is resource instance
  • How, Permission, defines the operation Permission of the subject on resources. The Permission is meaningless without resources, such as the user’s query Permission, the user’s add Permission, the call Permission of a certain class method, and the modification Permission of the user numbered 001, etc. By Permission, the subject can know the operation Permission of the resource

4.3 Authorization Mode

Role-based access control: Role-centered access control

if(subject.hasRole("admin")) {// What resource is operated on
}
Copy the code

Resource-based access control, resource-centered access control

if(subject.isPermission("user:update:01")) {// Resource instance
	// Modify user 01
}
if(subject.isPermission("user:update:*")) {// Resource type
	// Modify user 01
}
Copy the code

4.4 Permission String

The rules for permission strings are as follows: Resource identifier: Operation: Resource instance identifier, which means what operation on which instance of which resource, : is the resource/operation/instance separator, permission strings can also use * wildcard

Example:

  • User creation permission:user:createOr,user:create:*
  • User changed the permission of instance 001:user:update:001
  • All permissions for user instance 001:User: * : 001

4.5 Authorized programming implementation

On the basis of the previous MD5 encryption, realize the authorization operation

Custom CustomerMd5Realm

public class CustomerMd5Realm extends AuthorizingRealm {

    / / authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // Get the identity information
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        // Obtain the role and permission information of the current user from the database based on the identity information (user name)
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // Assign the role information queried in the database to the permission object
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");
        // Assign the permission information queried in the database to the permission object
        simpleAuthorizationInfo.addStringPermission("user:*:01");
        simpleAuthorizationInfo.addStringPermission("product:create:02");
        returnsimpleAuthorizationInfo; }... }Copy the code

Authorization logic

public class TestCustomerMd5RealmAuthenticator {

    public static void main(String[] args) {...// 7. Authenticate the user for authorization
        if (subject.isAuthenticated()) {
            // 7.1 Role-based Permission Control
            boolean hasRole = subject.hasRole("admin");
            System.out.println("Role verification:" + hasRole);
            7.2 Permission Control Based on Multiple Roles
            boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("admin"."user"));
            System.out.println("Multi-role verification:" + hasAllRoles);
            // 7.3 Whether to have one of the roles
            boolean[] booleans = subject.hasRoles(Arrays.asList("admin"."user"."super"));
            for (boolean aBoolean : booleans) {
                System.out.println(aBoolean);
            }
            // 7.4 Access control based on permission string
            boolean permitted = subject.isPermitted("user:*:01");
            System.out.println("Resource permission Verification:" + permitted);
            7.5 What resource rights does the Distribution have
            boolean[] permitted1 = subject.isPermitted("user:*:01"."order:*:10");
            for (boolean b : permitted1) {
                System.out.println(b);
            }
            // 7.6 What Resource Permissions Are Available at the same time
            boolean permittedAll = subject.isPermittedAll("user:*:01"."product:*");
            System.out.println("Multi-resource permission verification:"+ permittedAll); }}}Copy the code

5. Shiro integrates SpringBoot

Shiro’s idea of integrating SpringBoot is shown below:

Shiro was introduced to integrate SpringBoot dependencies

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.5.3</version>
</dependency>
Copy the code

5.1 certified

Configure shiro

@Configuration
public class ShiroConfig {

    // 1. Create shiroFilter to block all requests
    @Bean
    public ShiroFilterFactoryBean getShiroFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Set the security manager for filter
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // Configure system limited resources
        HashMap<String, String> map = new HashMap<>();
        map.put("/user/login"."anon");  // Set anon to a public resource
        map.put("/user/register"."anon");
        map.put("/register.jsp"."anon");
        map.put("/ * *"."authc"); // Authc indicates that requesting this resource requires authentication and authorization
        // Path of the default authentication interface
        shiroFilterFactoryBean.setLoginUrl("/login.jsp");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    2. Create a security manager
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("realm") Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // Set the Realm for the security manager
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    // create a custom realm
    @Bean(name = "realm")
    public Realm getRealm(a) {
        CustomerRealm customerRealm = new CustomerRealm();
        // Modify the credential verifier
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        // Set the encryption algorithm to MD5
        credentialsMatcher.setHashAlgorithmName("MD5");
        // Set the hash count
        credentialsMatcher.setHashIterations(1024);
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        returncustomerRealm; }}Copy the code
public class CustomerRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        UserSev userSev = (UserSev) ApplicationContextUtils.getBean("userSev");
        User user = userSev.findByUsername(principal);
        if(user ! =null) {
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()), this.getName());
        }
        return null; }}Copy the code

Shiro provides multiple default filters that you can use to control permissions for specific urls

Configuration for Corresponding filter function
anon AnonymousFilter Specifies that the URL can be accessed anonymously
authc FormAuthenticationFilter Specifies that the URL requires a form login, which is obtained from the request by defaultusername,password.rememberMeIf the login fails, the system switches to the loginUrl path. We can also use this filter as the default login logic, but we usually write our own login logic in the controller, so we can customize the message returned if we write it.
authcBasic BasicHttpAuthenticationFilter Basic login is required to specify the URL
logout LogoutFilter Logout filter, configure the specified URL can realize the exit function, very convenient
noSessionCreation NoSessionCreationFilter Disabling session creation
perms PermissionsAuthorizationFilter You need to specify permission to access
port PortFilter You need to specify a port for access
rest HttpMethodPermissionFilter Convert the HTTP request method into the corresponding verb to construct a permission string, this feeling is not meaningful, interested in looking at the source code comments
roles RolesAuthorizationFilter You need to specify a role to access
ssl SslFilter An HTTPS request is required for access
user UserFilter You need to be logged in or Remember Me to access it

Simulate the authentication, registration, and exit process

@Controller
@RequestMapping("user")
public class UserCon {

    @Autowired
    private UserSev userSev;

    @RequestMapping("logout")
    public String logout(String username, String password) {
        // Get the body object
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }

    @RequestMapping("login")
    public String login(String username, String password) {
        // Get the body object
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(new UsernamePasswordToken(username, password));
            return "redirect:/index.jsp";
        } catch (UnknownAccountException e) {
            System.out.println("User name error");
        } catch (IncorrectCredentialsException e) {
            System.out.println("Password error");
        }
        return "redirect:/index.jsp";
    }

    @RequestMapping("register")
    public String register(User user) {
        try {
            userSev.register(user);
        } catch (Exception e) {
            return "redirect:/register.jsp";
        }
        return "redirect:/login.jsp"; }}Copy the code
@Service
public class UserSev {

    @Autowired
    private UserDao userDao;

    public void register(User user) {
        // Process business calls to dao
        // The plaintext password is used for md5 + salt + hash
        String salt = SaltUtils.getSalt();
        user.setSalt(salt);
        Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);
        user.setPassword(md5Hash.toHex());
        userDao.save(user);
    }

    public User findByUsername(String username) {
        returnuserDao.findByUserName(username); }}Copy the code

5.2 license

The first way is through page tag authorization

The < %@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:hasAnyRoles name="user,admin">
    <li><a href=""> User management </a> <ul> <shiro:hasPermission name="user:add:*">
                <li><a href=""</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:delete:*">
                <li><a href=""</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:update:*">
                <li><a href=""</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:find:*">
                <li><a href=""> Query </a></li> </shiro:hasPermission> </ul> </li> </shiro:hasAnyRoles> <shiro:hasRole name="admin">
    <li><a href=""</a></li> <li><a href=""> Order management </a></li> <li><a href=""></ a></li> </shiro:hasRole>Copy the code

The second method is code authorization

@RequestMapping("save")
public String save(a){
  System.out.println("Method of entry");
  // Get the body object
  Subject subject = SecurityUtils.getSubject();
  // Code mode
  if (subject.hasRole("admin")) {
    System.out.println("Save order!");
  }else{
    System.out.println("No access!");
  }
  // Based on the permission string
  //....
  return "redirect:/index.jsp";
}
Copy the code

The second method is annotation authorization

@RequiresRoles(value={"admin","user"})// Check whether the role has admin user
@RequiresPermissions("user:update:01") // To determine the permission string
@RequestMapping("save")
public String save(a){
  System.out.println("Method of entry");
  return "redirect:/index.jsp";
}
Copy the code

This is just a demonstration. In real development, we need to persist authorization data. Three tables are required: user table, role table, and permission table. The relationship between user table and role table and between role table and permission table is many-to-many. You need to create a relationship table

Modify custom Realms

public class CustomerRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // Get the identity information
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();
        System.out.println("Invoke authorization authentication:"+primaryPrincipal);
        // Obtain role and permission information based on master identity information
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
        User user = userService.findRolesByUserName(primaryPrincipal);
        // Authorization role information
        if(! CollectionUtils.isEmpty(user.getRoles())){ SimpleAuthorizationInfo simpleAuthorizationInfo =new SimpleAuthorizationInfo();
            user.getRoles().forEach(role->{
                simpleAuthorizationInfo.addRole(role.getName());
                // Permission information
                List<Perms> perms = userService.findPermsByRoleId(role.getId());
                if(!CollectionUtils.isEmpty(perms)){
                    perms.forEach(perm->{
                        simpleAuthorizationInfo.addStringPermission(perm.getName());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
        return null; }}Copy the code

5.3 shiro cache

The use of cache can reduce the DB access pressure, so as to improve the efficiency of the system

5.3.1 integration Ehcache

Ehcache dependencies are introduced

<! -- Introducing shiro and Ehcache
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.5.3</version>
</dependency>
Copy the code

Open the cache

@Bean
public Realm getRealm(a){
    CustomerRealm customerRealm = new CustomerRealm();
    // Modify the credential verifier
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    // Set the encryption algorithm to MD5
    credentialsMatcher.setHashAlgorithmName("MD5");
    // Set the hash count
    credentialsMatcher.setHashIterations(1024);
    customerRealm.setCredentialsMatcher(credentialsMatcher);

    // Enable cache management
    customerRealm.setCacheManager(new EhCacheManager());
    customerRealm.setCachingEnabled(true);// Enable global caching
    customerRealm.setAuthenticationCachingEnabled(true);// Authentication authentication cache
    customerRealm.setAuthenticationCacheName("authenticationCache");
    customerRealm.setAuthorizationCachingEnabled(true);// Enable authorization caching
    customerRealm.setAuthorizationCacheName("authorizationCache");
    return customerRealm;
}
Copy the code

5.3.2 integrate Redis

Introduce redis dependencies

<! - integrate redis springboot -- -- >
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code

Configure the Redis connection and start the Redis service

spring.redis.port=6379
spring.redis.host=localhost
spring.redis.database=0
Copy the code

Ehcache provides EhCacheManager, and EhCacheManager implements the CacheManager interface, so we can customize a RedisCacheManager

public class RedisCacheManager implements CacheManager {
    @Override
    public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        System.out.println("Cache name:"+cacheName);
        return newRedisCache<K,V>(cacheName); }}Copy the code

Then customize the Cache interface implementation

public class RedisCache<K.V> implements Cache<K.V> {

    private String cacheName;

    public RedisCache(a) {}public RedisCache(String cacheName) {
        this.cacheName = cacheName;
    }

    @Override
    public V get(K k) throws CacheException {
        System.out.println("Get cache :"+ k);
        return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
    }

    @Override
    public V put(K k, V v) throws CacheException {
        System.out.println("Set cache key:"+k+" value:"+v);
        getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v);
        return null;
    }

    @Override
    public V remove(K k) throws CacheException {
        return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
    }

    @Override
    public v remove(k k) throws CacheException {
        return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
    }

    @Override
    public void clear(a) throws CacheException {
        getRedisTemplate().delete(this.cacheName);
    }

    @Override
    public int size(a) {
        return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
    }

    @Override
    public Set<k> keys(a) {
        return getRedisTemplate().opsForHash().keys(this.cacheName);
    }

    @Override
    public Collection<v> values(a) {
        return getRedisTemplate().opsForHash().values(this.cacheName);
    }

    private RedisTemplate getRedisTemplate(a){
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }


    // Encapsulate the redisTemplate
    private RedisTemplate getRedisTemplate(a){
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        returnredisTemplate; }}Copy the code

We also need a serialization implementation for SALT, and since the SimpleByteSource implementation provided with Shiro does not implement serialization, we need a custom salt serialization implementation

 // A custom salt implementation implements the serialization interface
 public class MyByteSource extends SimpleByteSource implements Serializable {
     public MyByteSource(String string) {
         super(string); }}Copy the code

Use custom salt in realm

public class MyByteSource implements ByteSource.Serializable {
  
    private  byte[] bytes;
    private String cachedHex;
    private String cachedBase64;

    // Add parameterless constructors for serialization and deserialization
    public MyByteSource(a){}public MyByteSource(byte[] bytes) {
        this.bytes = bytes;
    }

    public MyByteSource(char[] chars) {
        this.bytes = CodecSupport.toBytes(chars);
    }

    public MyByteSource(String string) {
        this.bytes = CodecSupport.toBytes(string);
    }

    public MyByteSource(ByteSource source) {
        this.bytes = source.getBytes();
    }

    public MyByteSource(File file) {
        this.bytes = (new MyByteSource.BytesHelper()).getBytes(file);
    }

    public MyByteSource(InputStream stream) {
        this.bytes = (new MyByteSource.BytesHelper()).getBytes(stream);
    }

    public static boolean isCompatible(Object o) {
        return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
    }

    public byte[] getBytes() {
        return this.bytes;
    }

    public boolean isEmpty(a) {
        return this.bytes == null || this.bytes.length == 0;
    }

    public String toHex(a) {
        if (this.cachedHex == null) {
            this.cachedHex = Hex.encodeToString(this.getBytes());
        }

        return this.cachedHex;
    }

    public String toBase64(a) {
        if (this.cachedBase64 == null) {
            this.cachedBase64 = Base64.encodeToString(this.getBytes());
        }

        return this.cachedBase64;
    }

    public String toString(a) {
        return this.toBase64();
    }

    public int hashCode(a) {
        return this.bytes ! =null && this.bytes.length ! =0 ? Arrays.hashCode(this.bytes) : 0;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (o instanceof ByteSource) {
            ByteSource bs = (ByteSource)o;
            return Arrays.equals(this.getBytes(), bs.getBytes());
        } else {
            return false; }}private static final class BytesHelper extends CodecSupport {
        private BytesHelper(a) {}public byte[] getBytes(File file) {
            return this.toBytes(file);
        }

        public byte[] getBytes(InputStream stream) {
            return this.toBytes(stream); }}}Copy the code