An overview,

1. Introduce Shiro

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, password, and session management. With Shiro’s easy-to-understand apis, you can quickly and easily get any application, from the smallest mobile application to the largest web and enterprise applications.

2. Basic functions

  • Authentication: Authentication/login to verify that the user has the appropriate identity;

  • Authorization: indicates Authorization. Authorization is used to verify whether an authenticated user has a certain permission. That is, it determines whether a user can do something. For example, it verifies whether a user has a role. Or fine-grained verification of whether a user has a certain permission on a certain resource;

  • Session Management: Session Management, that is, after a user logs in to a Session, all its information is in the Session before the user logs out. The session can be in a normal JavaSE environment or in a Web environment.

  • Cryptography: To protect the security of data. For example, passwords are encrypted and stored in a database instead of being stored in plain text.

  • Supported features:

    • Web Support, which can be easily integrated into Web environment;

    • Caching: indicates the cache. For example, after a user logs in, the user information and roles and permissions do not need to be checked every time. This improves efficiency.

    • Concurrency: Shiro supports Concurrency validation for multi-threaded applications. Let’s say you start another thread in one thread and automatically propagate permissions to the other.

    • Testing: Provide Testing support;

    • Run As: Allows a user to pretend to be another user if they allow access;

    • Remember Me: This is a very common function, that is, once logged in, the next time you do not have to log in.

Note: Shiro does not maintain users or privileges; These need to be designed/provided by us; Then inject it into Shiro through the corresponding interface.

3. The architecture

  • Subject: the principal, as you can see, can be any “user” that can interact with the application;

  • SecurityManager: Equivalent to DispatcherServlet in SpringMVC or FilterDispatcher in Struts2; It’s Shiro’s heart; All concrete interactions are controlled by the SecurityManager; It manages all subjects and is responsible for authentication and authorization, session and cache management.

  • Authenticator: The Authenticator, responsible for principal authentication, is an extension point that users can customize if they feel Shiro’s default is not good; The Authentication Strategy is required, that is, when the user passes the Authentication;

  • Authrizer: An authorizer, or access controller, that determines whether the principal has permission to perform the corresponding operation; That is, it controls what functionality the user can access in the application;

  • Realm: There can be one or more realms that can be considered security entity data sources, i.e. used to retrieve security entities; It can be a JDBC implementation, it can be an LDAP implementation, it can be a memory implementation, etc. Provided by the user; Note: Shiro does not know where your users/permissions are stored and in what format; So we generally need to implement our own realms in our applications;

  • SessionManager: If you’ve ever written a Servlet, you know the concept of a Session. A Session needs someone to manage its lifecycle. This component is the SessionManager. Shiro can be used not only in Web environment, but also in ordinary Java ASE environment, EJB environment and so on. So Shiro abstracts its own Session to manage the data that the principal interacts with the application; So in this case, for example, if we’re using a Web environment, we’re starting with a Web server; Then I went to an EJB server; If you want to put session data from both servers in one place, you can implement your own distributed session (such as Memcached).

  • If you want to save your Session to a database, you can implement your own SessionDAO. If you want to save your Session to a database, you can write to the database via JDBC. If you want to put your Session in Memcached, you can implement your own Memcached SessionDAO; In addition, SessionDAO can be cached using Cache to improve performance.

  • CacheManager: cache controller, which manages the caches of users, roles, and permissions. Since this data is rarely changed, putting it in the cache can improve access performance

  • Cryptography: The Cryptography module, Shiro improves some of the common Cryptography components for use such as Cryptography/decryption.

For us, the simplest application of Shiro is:

  1. Application code authenticates and authorizes through a Subject, which delegates to the SecurityManager;

  2. We need to inject a Realm into Shiro’s SecurityManager so that the SecurityManager can get legitimate users and their permissions to determine.

Ii. Authentication (Login)

1. Introduce dependencies

Maven warehouse address: mvnrepository.com/artifact/or…

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

2. Common classes in Shiro

(1) Common classes

Common Shiro classes or interfaces:

The name type instructions
SecurityManager interface A security manager that authenticates and authorizes principals, plus the ability to log in, log out, and create users
DefaultWebSecurityManager object The default Web SecurityManager provides some default values
ShiroFilterFactoryBean object Shiro’s filter object that will configure Shiro’s interception of a rule
Realm interface Data source from which authentication and authorization-related data is accessed
AuthenticatingRealm An abstract class Only responsible for authentication (login), by implementing the class to specify the authentication method
AuthorizingRealm An abstract class Responsible for authentication (login) and authorization, which are specified by implementing this class
AuthenticationToken interface Logged in to unauthenticated data
AuthenticationInfo interface Account information related to the authentication/login process.
Subject interface The program uses this object to manipulate Shiro

(2) Common anomalies

Exception class instructions
AuthenticationException Superclass of all exceptions in Shiro
AccountException Account exceptions can be thrown by ourselves
UnknownAccountException Exceptions where the account does not exist can be thrown by ourselves
LockedAccountException The account lock exception can be thrown by ourselves
IncorrectCredentialsException Password error Exception This exception is thrown during Shiro password authentication

3. Use Shiro to authenticate the account

1. The Realm interface

Shiro gets security data (such as users, roles, and permissions) from a Realm. This means that SecurityManager needs to authenticate the user, then it needs to get the corresponding user from a Realm and compare it to determine if the user’s identity is valid. You also need to get the user’s roles/permissions from the Realm to verify that the user can perform operations; A Realm can be thought of as a DataSource, a secure DataSource.

public interface Realm {
	// Return a unique Realm name
    String getName(a);

	// In a Realm, determine whether this Realm supports the Token
    boolean supports(AuthenticationToken token);

	// Return the authenticated AuthenticationInfo based on the unauthenticated AuthenticationToken
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

}
Copy the code

Custom Realm class

Two common abstract classes that already implement the Realm interface:

  • AuthenticatingRealm: Only responsible for authentication (login)
  • AuthorizingRealm: Responsible for authentication (login) and authorization, yesAuthenticatingRealmA subclass of

Here you use the AuthenticatingRealm first, followed by authentication followed by AuthorizingRealm

public class MyRealm extends AuthenticatingRealm {
    /** * Shiro authentication method we need in this method to get user information (from the database) *@paramAuthenticationToken The Token that the user uses to log in. This object will hold our user's account and password * in the browser@returnReturns an AuthenticationInfo object. Upon return, Shiro calls some methods in this object to complete the authentication of the password, which Shiro verifies to be valid *@throwsAuthenticationException Shiro throws AuthenticationException */ if authentication fails
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // Convert AuthenticationToken to UsernamePasswordToken to make it easier to get the account and password
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // Get the account that the user entered in the browser
        String username = token.getUsername();
        
        // To authenticate the account, normally we need to obtain the account information from the database, and other key data, such as whether the account has been frozen, etc
        String dbusername = username;
        if (!"admin".equals(dbusername) && !"zhangsan".equals(dbusername)) {// Check whether the user account is correct
            throw new UnknownAccountException("Account number error");
        }
        if ("zhangsan".equals(username)) {
            throw new LockedAccountException("Account is locked.");
        }
        
        // Define a password. The password should come from the database
        // If you want to get it from the database, you can define UserMapper and then get it from the database
        String dbpassword = "123456";
        
        // Check whether the authentication password is correct
        return newSimpleAuthenticationInfo(dbusername, dbpassword, getName()); }}Copy the code

3. ShiroConfig

Define ShiroConfig

@Configuration
public class ShiroConfig {
    // Configure Shiro's security manager
    @Bean
    public SecurityManager securityManager(Realm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // Set up a Realm, which is the actual object that will eventually be used to complete our authentication and authorization operations
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    // Configure a custom Realm bean that will eventually use the objects returned by the bean to complete our authentication and authorization
    @Bean
    public Realm myRealm(a) {
        MyRealm realm = new MyRealm();
        return realm;
    }

    // Configure a Shiro filter bean. This bean will configure Shiro for intercepting one of Shiro's rules
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        // Create Shiro's interceptor to intercept our user requests
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        // Set Shiro's security management. Setting Shiro's security management also specifies a Realm where we can assign permissions
        shiroFilter.setSecurityManager(securityManager);

        // To set a login request address, which can be an HTML or JSP access path, or a controller path
        // This is used to notify Shiro that we can use this path to redirect to the login page. Shiro will automatically redirect to this path when Shiro determines that our current user is not logged in
        shiroFilter.setLoginUrl("/");
        // After a successful login, go to the page
        shiroFilter.setSuccessUrl("/success");
        // Used to specify that there is no permission to redirect to the page
        shiroFilter.setUnauthorizedUrl("/noPermission");
        
        // Define a set of maps that contain rules that tell Shiro what requests are accessible and what requests are not
        / * * anon: without certification can access * authc: must be certified to visit * user: must be used with remember function can I use * perms: have access to a resource permissions to * role: have a role permissions to access * logout: Logout frees memory */ for the current user
        Map<String, String> map = new LinkedHashMap<String, String>();
        // '/login' indicates the name of a request; 'anon' indicates permission
        map.put("/login"."anon");
        map.put("/logout"."logout");
        // Note: ** indicates any descendant path
        // * indicates an arbitrary path
        / /? Represents an arbitrary character
        map.put("/admin/**"."authc");
        map.put("/user/**"."authc");

        This must be written at the end of the Map set. This option is optional. If this is not specified, Shiro will block the request if it does not comply with the above interception rules
        map.put("/ * *"."authc");
        shiroFilter.setFilterChainDefinitionMap(map);
        returnshiroFilter; }}Copy the code

4. UserController

@Controller
public class TestController {
    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        
        // Create a Shiro Subject object and use this object to complete user login authentication
        //Subject can be understood as the current user. Shiro provides its own Session
        Subject subject = SecurityUtils.getSubject();

        // Check whether the user has been authenticated (whether the user has logged in). If is entered, authentication is required
        if(! subject.isAuthenticated()) {// Create a Token object for the user account and password and set the account and password entered by the user. This object will be fetched in Shiro
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            try {

                // After login is called, Shiro automatically executes our custom Realm authentication methods
                subject.login(token);

            } catch (UnknownAccountException e) {
                // The user's account error exception can be seen in the MyRealm class above, which I threw myself in the background
                model.addAttribute("errorMessage"."Account number does not exist.");
                return "login";
            } catch (LockedAccountException e) {
                // The account is locked exception. You can see MyRealm class above, which is thrown by me in the background
                model.addAttribute("errorMessage"."The account has been frozen.");
                return "login";
            } catch (IncorrectCredentialsException e) {
                // The user password error exception is thrown by Shiro when authenticating the password
                model.addAttribute("errorMessage"."Password error");
                return "login"; }}// If the login is successful, the login success page is displayed
        return "success";
    }

	Map.put ("/logout", "logout"); map.put("/logout", "logout"); * 2. Use the Subject logout() as follows: */
    @RequestMapping("/logout")
    public String logout(a) {
        Subject subject = SecurityUtils.getSubject();
        
		// Log out
        subject.logout();

        return "login"; }}Copy the code

5. Password encryption (understand)

public class MyRealm extends AuthenticatingRealm {
    /** * Shiro authentication method we need in this method to get user information (from the database) *@paramAuthenticationToken The Token that the user uses to log in. This object will hold our user's account and password * in the browser@returnReturns an AuthenticationInfo object. Upon return, Shiro calls some methods in this object to complete the authentication of the password, which Shiro verifies to be valid *@throwsAuthenticationException Shiro throws AuthenticationException */ if authentication fails
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // Convert AuthenticationToken to UsernamePasswordToken to make it easier to get the account and password
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // Get the account that the user entered in the browser
        String username = token.getUsername();
        
        // To authenticate the account, normally we need to obtain the account information from the database, and other key data, such as whether the account has been frozen, etc
        String dbusername = username;
        if (!"admin".equals(dbusername) && !"zhangsan".equals(dbusername)) {// Check whether the user account is correct
            throw new UnknownAccountException("Account number error");
        }
        if ("zhangsan".equals(username)) {
            throw new LockedAccountException("Account is locked.");
        }
        // Define a password. The password should come from the database
        String dbpassword = "123456";

		// Parameter 1: indicates the encryption algorithm. Select MD5 encryption here
        // Parameter 2: indicates the data to be encrypted
        // Parameter 3: indicates the salt value during encryption. It is used to change the encrypted data result. Usually, the salt value needs to select a unique data in the table, such as the account in the table
        // Parameter 4: specifies how many times the data needs to be encrypted using the specified algorithm
        Object obj = new SimpleHash("MD5", dbpassword, "".1);
        // Check whether the authentication password is correct. Use the encrypted password for login
        return newSimpleAuthenticationInfo(dbusername, obj.toString(), getName()); }}Copy the code

Three, authorization,

1. Configure permissions in a Realm

1. Customize the Realm class

The AuthorizingRealm class extends the AuthorizingRealm abstract class. The AuthorizingRealm class adds an authorized abstract method to the AuthenticatingRealm class.

public class MyRealm extends AuthorizingRealm {
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // Do the authentication here, see custom Realm in authentication (II.3.2)
    }

    @Override
    //Shiro user authorization callback method
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // Get the user name from Shiro
        Object username = principalCollection.getPrimaryPrincipal();
        
        // Create an object of the SimpleAuthorizationInfo class, using which we need to set the permission information of the current user
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        
        // Set of user role information (from database)
        Set<String> roles = new HashSet<>();
        if ("admin".equals(username)) {
        	// Indicates that the user has the admin and user roles
            roles.add("admin");
            roles.add("user");
        } else if ("zhangsan".equals(username)) {
        	// Indicates that the user has only the user role
            roles.add("user");
        }

        // User permission information (from database)
        Set<String> psermission = new HashSet<>();
        if ("admin".equals(username)) {
        	// Indicates that the user has the add permission for admin
            psermission.add("admin:add");
        }
        
        // Set role and permission information
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(psermission);
        returnsimpleAuthorizationInfo; }}Copy the code

2. In ShiroConfig, configure path permission rules

@Configuration
public class ShiroConfig {
    // Configure Shiro's security manager
    @Bean
    public SecurityManager securityManager(Realm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // Set up a Realm, which is the actual object that will eventually be used to complete our authentication and authorization operations
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    // Configure a custom Realm bean that will eventually use the objects returned by the bean to complete our authentication and authorization
    @Bean
    public Realm myRealm(a) {
        MyRealm realm = new MyRealm();
        return realm;
    }

    // Configure a Shiro filter bean. This bean will configure Shiro for intercepting one of Shiro's rules
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        // Create Shiro's interceptor to intercept our user requests
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        // Set Shiro's security management. Setting Shiro's security management also specifies a Realm where we can assign permissions
        shiroFilter.setSecurityManager(securityManager);

        // To set a login request address, which can be an HTML or JSP access path, or a controller path
        // This is used to notify Shiro that we can use this path to redirect to the login page. Shiro will automatically redirect to this path when Shiro determines that our current user is not logged in
        shiroFilter.setLoginUrl("/");
        // After a successful login, go to the page
        shiroFilter.setSuccessUrl("/success");
        // Used to specify that there is no permission to redirect to the page
        shiroFilter.setUnauthorizedUrl("/noPermission");
        
        // Define a set of maps that contain rules that tell Shiro what requests are accessible and what requests are not
        / * * anon: without certification can access * authc: must be certified to visit * user: must be used with remember function can I use * perms: have access to a resource permissions to * role: have a role permissions to access * logout: Logout frees memory */ for the current user
        Map<String, String> map = new LinkedHashMap<String, String>();
        // '/login' indicates the name of a request; 'anon' indicates permission
        map.put("/login"."anon");
        map.put("/logout"."logout");
        //roles[admin] Indicates that a request starting with /admin/** can be accessed only by the admin role
        //perms[admin:add] Indicates that the request for /admin/test can be accessed only with the admin:add permission
        // Note: permissions are matched from top to bottom, so /admin/test is written before /admin/**
        map.put("/admin/test"."authc,perms[admin:add]");
        map.put("/admin/**"."authc,roles[admin]");
        map.put("/user/**"."authc,roles[user]");
        map.put("/ * *"."authc");

        shiroFilter.setFilterChainDefinitionMap(map);
        returnshiroFilter; }}Copy the code

2. Annotation-based permission control

1. Enable annotation support in ShiroConfig

== Note == : After enabling permission control for annotations, you need to delete the Shiro configuration rules for permission interception

@Configuration
public class ShiroConfig {

    @Bean
    public MyRealm myRealm(a) {
        return new MyRealm();
    }

    @Bean
    public SecurityManager securityManager(MyRealm myRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager(myRealm);

        return manager;
    }

    // Configure a Shiro filter bean. This bean will configure Shiro for intercepting one of Shiro's rules
    // For example, what requests can be accessed and what requests cannot be accessed
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        shiroFilterFactory.setSecurityManager(securityManager);

        // Login page
        shiroFilterFactory.setLoginUrl("/");
        // The address to be accessed after a successful login
        shiroFilterFactory.setSuccessUrl("/success");
        // Address accessed when login failed
        shiroFilterFactory.setUnauthorizedUrl("/nopermission");

        /** * Configure permission interception rules */
        Map<String, String> map = new LinkedHashMap<>();

        map.put("/login"."anon");
        map.put("/logout"."logout");

        // Delete all roles and permissions from ShrioConfig
        //map.put("/admin/add", "authc,perms[admin:add]");
        //map.put("/admin/**", "authc,roles[admin]");
        //map.put("/user/**", "authc,roles[user]");

        shiroFilterFactory.setFilterChainDefinitionMap(map);

        return shiroFilterFactory;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(a) {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /** * Enable AOP support **@param securityManager
     * @return* /
    @Bean
    public AuthorizationAttributeSourceAdvisor advisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        returnadvisor; }}Copy the code

2. Use annotations

  • @requiresauthentication: Indicates that the current Subject has been authenticated by login; Subject.isauthenticated () returns true.

  • @requiresUser: indicates that the current Subject is authenticated or by remembering what I logged in to.

  • @requiresguest: Indicates that the current Subject is not authenticated or is a tourist by remembering that I logged in.

  • @RequiresRoles(String[] value, Logical logical)

    • value: List of roles. Such as:Value = {" admin ", "user"}
    • logical: Relationships to be satisfied between multiple roles (Logical.ANDLogical.OR), the default isLogical.AND
  • @RequiresPermissions (String[] value, Logical logical)

    • value: Indicates the role permission list. Such as:Value = {" user: a ", "user: b}"
    • logical: Relationships to be satisfied between multiple roles (Logical.ANDLogical.OR), the default isLogical.AND
@Controller
public class TestController {

    @RequestMapping("/nopermission")
    public String noPermission(a) {
        return "noPermission";
    }

    // The annotation for RequiresRoles Shiro indicates that the admin role is required to access the function
    // Note that you need to configure Spring's declarative exception catching after using annotations. Otherwise, you will see Shiro's error message in the browser instead of the friendly message prompt
    @RequiresRoles(value = {"admin"})
    @RequestMapping("/admin/test")
    public @ResponseBody String adminTest(a) {
        return "This adminTest request";
    }

    @RequiresPermissions(value = {"admin:add"})
    @RequestMapping("/admin/add")
    public @ResponseBody String adminAdd(a) {
        Subject subject = SecurityUtils.getSubject();
        // Verify that the current user has this permission
        //subject.checkPermission();
        // Verify that the current user has the role
        subject.checkRole("admin:add");
        return "This adminAdd request";
    }

    // Configure a Spring exception monitor that will go directly to the current method when the project throws all the exception types specified by value
    @ExceptionHandler(value = {Exception.class})
    public String myError(Throwable throwable) {
        // Obtain the exception type. Different information is displayed when you enter an invalid page according to the exception type
        System.out.println(throwable.getClass());
        return "noPermission"; }}Copy the code

4. Session Management (Redis)

1. Integrate Redis

  1. Add a POM file
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code
  1. Configure redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123
Copy the code

2. Rewrite the SessionDAO

public class MyRedisSessionDAO extends AbstractSessionDAO {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    public static final int DEFAULT_TIME_OUT = 30;
    public static final String SESSION_PREFIX = "shiro_session";

    @Override
    protected Serializable doCreate(Session session) {
        / / generated SessionId
        Serializable sessionId = this.generateSessionId(session);
        // The generated ID must be set to the session strength
        this.assignSessionId(session, sessionId);

        // Save the session to redis
        this.saveSession(session);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        if(sessionId ! =null) {
            Session session = (Session) redisTemplate.opsForValue().get(SESSION_PREFIX + sessionId);
            return session;
        }
        return null;
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        this.saveSession(session);
    }

    @Override
    public void delete(Session session) {
        if(session ! =null) { redisTemplate.delete(SESSION_PREFIX + session.getId()); }}// Return all sessions
    @Override
    public Collection<Session> getActiveSessions(a) {
        Set<Object> keys = redisTemplate.keys(SESSION_PREFIX + "*");

        Set<Session> set = keys.parallelStream()
                .map(key -> (Session) redisTemplate.opsForValue().get(key))
                .collect(Collectors.toSet());

        return set;
    }

    // Save the session to redis
    private void saveSession(Session session) {
        if(session ! =null&& session.getId() ! =null) {
            redisTemplate.opsForValue().set(SESSION_PREFIX + session.getId(), session, 30, TimeUnit.MINUTES); }}}Copy the code

3. To be rewrittenSessionDAOConfiguration toSecurityManager

@Configuration
public class ShiroConfig {

    @Bean
    public MyRealm myRealm(a) {
        return new MyRealm();
    }

    // Create your own SessionDAO instance
    @Bean
    public SessionDAO myRedisSessionDAO(a) {
        return new MyRedisSessionDAO();
    }

    // Create a Session managed object using a custom SessionDao
    @Bean
    public SessionManager sessionManager(SessionDAO myRedisSessionDAO) {
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        manager.setSessionDAO(myRedisSessionDAO);
        return manager;
    }

    @Bean
    public SecurityManager securityManager(MyRealm myRealm, SessionManager sessionManager) {
        / / configuration Realm
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager(myRealm);
        / / configuration SessioonManager
        manager.setSessionManager(sessionManager);

        return manager;
    }

    // Configure a Shiro filter bean. This bean will configure Shiro for intercepting one of Shiro's rules
    // For example, what requests can be accessed and what requests cannot be accessed
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        shiroFilterFactory.setSecurityManager(securityManager);
        // Login page
        shiroFilterFactory.setLoginUrl("/");
        // The address to be accessed after a successful login
        shiroFilterFactory.setSuccessUrl("/success");
        // Address accessed when login failed
        shiroFilterFactory.setUnauthorizedUrl("/nopermission");

        /** * Configure permission interception rules */
        Map<String, String> map = new LinkedHashMap<>();

        map.put("/login"."anon");
        map.put("/logout"."logout");

        // Delete all roles and permissions from ShrioConfig
        map.put("/admin/add"."authc,perms[admin:add]");
        map.put("/admin/**"."authc,roles[admin]");
        map.put("/user/**"."authc,roles[user]");

        shiroFilterFactory.setFilterChainDefinitionMap(map);

        returnshiroFilterFactory; }}Copy the code

4. Check the Redis

5. Cache (Redis)

1. Create CacheManager

The CacheManager needs to provide a Cache object. Shiro provides spring-like Cache abstraction. That is, Shiro does not implement the Cache itself, but abstracts the Cache to facilitate the replacement of different underlying Cache implementations.

public class MyRedisCacheManager extends AbstractCacheManager {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    protected Cache createCache(String s) throws CacheException {
        return new RedisCache();
    }

	// Create a RedisCache object to implement the Cache interface
    class RedisCache<K.V> implements Cache<K.V> {

        public static final String CACHE_KEY = "redis_cache";

        @Override
        public V get(K k) throws CacheException {
            return (V) redisTemplate.boundHashOps(CACHE_KEY).get(k);
        }

        @Override
        public V put(K k, V v) throws CacheException {
            redisTemplate.boundHashOps(CACHE_KEY).put(k, v);
            return v;
        }

        @Override
        public V remove(K k) throws CacheException {
            V v = get(k);
            redisTemplate.boundHashOps(CACHE_KEY).delete(k);
            return v;
        }

        @Override
        public void clear(a) throws CacheException {
            redisTemplate.delete(CACHE_KEY);
        }

        @Override
        public int size(a) {
            return (int) (long) redisTemplate.boundHashOps(CACHE_KEY).size();
        }

        @Override
        public Set<K> keys(a) {
            return redisTemplate.boundHashOps(CACHE_KEY).keys();
        }

        @Override
        public Collection<V> values(a) {
            returnredisTemplate.boundHashOps(CACHE_KEY).values(); }}}Copy the code

2. Configuration ShiroConfig

@Configuration
public class ShiroConfig {
	
	// Create a custom CacheManager object
    @Bean
    public CacheManager cacheManager(a) {
        return new MyRedisCacheManager();
    }
    
    // Set the CacheManager of SecurityManager to the customized MyRedisCacheManager
    @Bean
    public SecurityManager securityManager(MyRealm myRealm, CacheManager cacheManager) {
        / / configuration Realm
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager(myRealm);
        manager.setCacheManager(cacheManager);

        return manager;
    }
    
	@Bean
    public MyRealm myRealm(a) {
        return new MyRealm();
    }
Copy the code

3. Check the Redis

Looking at Redis, it is generally clear that Shiro caches permission information