SpringBoot integrates Shiro for dynamic permission loading updates + Session sharing + single sign-on

Keywords: Java Shiro Redis Session Spring

Author: Sans_

juejin.im/post/5d087d605188256de9779e64

1. Description

Shiro is a security framework that is primarily used in projects for authentication, authorization, encryption, and user session management. Although Shiro is not as rich as SpringSecurity, it is lightweight and simple, and Shiro is generally qualified for business needs in projects.

2. Project environment

  • MyBatis-Plus version: 3.1.0

  • SpringBoot version: 2.1.5

  • JDK version: 1.8

  • Shiro version: 1.4

  • Shiro-redis Plug-in Version: 3.1.0

Data table (SQL file in project): The passwords for test numbers in the database are encrypted, all of which are 123456

Maven relies on the following:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- AOP rely on,Be sure to add,Otherwise, permission interception validation will not take effect -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- lombok Plug-in unit -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        <!-- mybatisPlus Core Library -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!-- Introduce Ali Database Connection Pool -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>
        <!-- Shiro Core Dependency -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- Shiro-redis Plug-in unit -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!-- StringUitlS tool -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
</dependencies>

The configuration is as follows:

# Configure Port
server:
  port: 8764
spring:
# Configure Data Source
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/my_shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
# Redis data source
  redis:
    host: localhost
    port: 6379
    timeout: 6000
    password: 123456
    jedis:
      pool:
max-active: 1000 # Maximum number of connections to the connection pool (use a negative value to indicate no limit)
max-wait: -1 # Maximum blocking wait time for connection pool (use a negative value to indicate no limit)
* max-idle: 10 # Maximum idle connection in connection pool
Minimum idle connection in connection pool
# mybatis-plus related configuration
mybatis-plus:
# XML scan, with multiple directories separated by commas or semicolons (telling Mapper the location of the corresponding XML file)
  mapper-locations: classpath:mapper/*.xml
The following configurations have default values, which can be set without
  global-config:
    db-config:
#Primary Key Type AUTO:'Database ID Self-Increasing'INPUT:'User Input ID', ID_WORKER:'Global Unique ID (Number Type Unique ID)', UUID:'Global Unique ID UUID';
      id-type: auto
#Field Policy IGNORED:'Ignore Judgment'(NOT_NULL:'Non-NULL Judgment') NOT_EMPTY:'Non-empty Judgment'
      field-strategy: NOT_EMPTY
#Database type
      db-type: MYSQL
  configuration:
# Turn on Automatic Hump naming rule mapping: a similar mapping from database column names to Java attribute hump naming
    map-underscore-to-camel-case: true
# If a column with null values is included in the query result, MyBatis will not map this field when mapping
    call-setters-on-nulls: true
# This configuration prints the sql executed and can be used during development or testing
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2. Write project base classes

User entities, Dao,Service, etc. are omitted here, see source code

Write an Exception class to handle Shiro privilege interception exceptions

 

Create SHA256Util Encryption Tool

 

Create Spring Tool

/**
 * @Description Spring Context Tool Class
 * @Author Sans
 * @CreateTime 2019/6/17 13:40
 */
@Component
public class SpringUtil implements ApplicationContextAware {
    private static ApplicationContext context;
    /**
     * Spring Determines if the bean is a subclass of ApplicationContextAware after initialization
     * If the class is the setApplicationContext() method, the ApplicationContext in the container is passed in as a parameter
     * @Author Sans
     * @CreateTime 2019/6/17 16:58
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
    /**
     * Returns the specified Bean through Name
     * @Author Sans
     * @CreateTime 2019/6/17 16:03
     */
    public static <T> getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }
}

Create Shiro Tool

/**
 * @Description Shiro Tool class
 * @Author Sans
 * @CreateTime 2019/6/15 16:11
 */
public class ShiroUtils {

    /** Private Constructor **/
    private ShiroUtils(){}

    private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class);

    /**
     * Get the current user Session
     * @Author Sans
     * @CreateTime 2019/6/17 17:03
     * @Return SysUserEntity User Information
     */
    public static Session getSession() {
        return SecurityUtils.getSubject().getSession();
    }

    /**
     * Logout
     * @Author Sans
     * @CreateTime 2019/6/17 17:23
     */
    public static void logout() {
        SecurityUtils.getSubject().logout();
    }

    /**
    * Get current user information
    * @Author Sans
    * @CreateTime 2019/6/17 17:03
    * @Return SysUserEntity User Information
    */
    public static SysUserEntity getUserInfo() {
      return (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
    }

    /**
     * Delete user cache information
     * @Author Sans
     * @CreateTime 2019/6/17 13:57
     * @Param  username  User Name
     * @Param  isRemoveSession Whether to delete Session
     * @Return void
     */
    public static void deleteCache(String username, boolean isRemoveSession){
        //Get Session from Cache
        Session session = null;
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        SysUserEntity sysUserEntity;
        Object attribute = null;
        for(Session sessionInfo : sessions){
            //Traverse Session to find the Session corresponding to the user name
            attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }
            sysUserEntity = (SysUserEntity) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (sysUserEntity == null) {
                continue;
            }
            if (Objects.equals(sysUserEntity.getUsername(), username)) {
                session=sessionInfo;
            }
        }
        if (session == null||attribute == null) {
            return;
        }
        //Delete session
        if (isRemoveSession) {
            redisSessionDAO.delete(session);
        }
        //Delete Cache and reauthorize when accessing restricted interfaces
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        Authenticator authc = securityManager.getAuthenticator();
        ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
    }
}

Create Shiro's SesionId Generator

3. Writing Shiro Core Classes

Create Realm for Authorization and Authentication

/**
 * @Description Shiro Privilege matching and account password matching
 * @Author Sans
 * @CreateTime 2019/6/15 11:27
 */
public class ShiroRealm extends AuthorizingRealm {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    /**
     * Authorization Permissions
     * Shiro looks in the cache when the user authenticates permissions, and if no data is found, executes this method to look up permissions and put them in the cache
     * @Author Sans
     * @CreateTime 2019/6/12 11:44
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        SysUserEntity sysUserEntity = (SysUserEntity) principalCollection.getPrimaryPrincipal();
        //Get User ID
        Long userId =sysUserEntity.getUserId();
        //Authorization and processing are available here
        Set<String> rolesSet = new HashSet<>();
        Set<String> permsSet = new HashSet<>();
        //Query roles and permissions (self-querying here based on business)
        List<SysRoleEntity> sysRoleEntityList = sysRoleService.selectSysRoleByUserId(userId);
        for (SysRoleEntity sysRoleEntity:sysRoleEntityList) {
            rolesSet.add(sysRoleEntity.getRoleName());
            List<SysMenuEntity> sysMenuEntityList = sysMenuService.selectSysMenuByRoleId(sysRoleEntity.getRoleId());
            for (SysMenuEntity sysMenuEntity :sysMenuEntityList) {
                permsSet.add(sysMenuEntity.getPerms());
            }
        }
        //Pass the discovered permissions and roles into authorizationInfo, respectively
        authorizationInfo.setStringPermissions(permsSet);
        authorizationInfo.setRoles(rolesSet);
        return authorizationInfo;
    }

    /**
     * identity authentication
     * @Author Sans
     * @CreateTime 2019/6/12 12:36
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //Get the user's input account.
        String username = (String) authenticationToken.getPrincipal();
        //Find the User object from the database through username, and verify if found
        //In a real project, the cache can be cached according to the actual situation. If not, Shiro also has a time interval mechanism and will not repeat this method in 2 minutes.
        SysUserEntity user = sysUserService.selectUserByName(username);
        //Determine if an account exists
        if (user == null) {
            throw new AuthenticationException();
        }
        //Determine if accounts are frozen
        if (user.getState()==null||user.getState().equals("PROHIBIT")){
            throw new LockedAccountException();
        }
        //Verify
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user,                                  //User name
                user.getPassword(),                    //Password
                ByteSource.Util.bytes(user.getSalt()), //Set salt value
                getName()
        );
        //Verify successful start kicking (clear cache and ession)
        ShiroUtils.deleteCache(username,true);
        return authenticationInfo;
    }
}

Create SessionManager Class

Create ShiroConfig Configuration Class

/**
 * @Description Shiro Configuration Class
 * @Author Sans
 * @CreateTime 2019/6/10 17:42
 */
@Configuration
public class ShiroConfig {

    private final String CACHE_KEY = "shiro:cache:";
    private final String SESSION_KEY = "shiro:session:";
    private final int EXPIRE = 1800;

    //Redis Configuration
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.password}")
    private String password;

    /**
     * Turn on Shiro-aop annotation support
     * @Attention Use proxy mode so code support needs to be turned on
     * @Author Sans
     * @CreateTime 2019/6/12 8:38
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro Basic Configuration
     * @Author Sans
     * @CreateTime 2019/6/12 8:42
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //Note that the filter configuration order cannot be reversed
        //Configure filtering: links that will not be blocked
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/userLogin/**", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        // Configure shiro default login interface address, login interface jump in front-end and back-end separation should be controlled by front-end routing, background only returns json data
        shiroFilterFactoryBean.setLoginUrl("/userLogin/unauth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * Security Manager
     * @Author Sans
     * @CreateTime 2019/6/12 10:34
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //Custom Ssession Management
        securityManager.setSessionManager(sessionManager());
        //Custom Cache implementation
        securityManager.setCacheManager(cacheManager());
        //Custom Realm Validation
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }

    /**
     * Authenticator
     * @Author Sans
     * @CreateTime 2019/6/12 10:37
     */
    @Bean
    public ShiroRealm shiroRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

    /**
     * Voucher Matcher
     * Password verification is handed over to Shiro's SimpleAuthenticationInfo for processing, where matching configurations are made
     * @Author Sans
     * @CreateTime 2019/6/12 10:48
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();
        //Hash algorithm: SHA256 algorithm is used here;
        shaCredentialsMatcher.setHashAlgorithmName(SHA256Util.HASH_ALGORITHM_NAME);
        //Number of hashes, such as two, equal to md5(md5("));
        shaCredentialsMatcher.setHashIterations(SHA256Util.HASH_ITERATIONS);
        return shaCredentialsMatcher;
    }

    /**
     * Configure Redis Manager
     * @Attention Using the shiro-redis open source plug-in
     * @Author Sans
     * @CreateTime 2019/6/12 11:06
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * Configure Cache Manager
     * Used to store permissions and role identities to Redis
     * @Attention Using the shiro-redis open source plug-in
     * @Author Sans
     * @CreateTime 2019/6/12 12:37
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix(CACHE_KEY);
        //Configuring caches requires that entity classes placed in session s must have an id identity
        redisCacheManager.setPrincipalIdFieldName("userId");
        return redisCacheManager;
    }

    /**
     * SessionID generator
     * @Author Sans
     * @CreateTime 2019/6/12 13:12
     */
    @Bean
    public ShiroSessionIdGenerator sessionIdGenerator(){
        return new ShiroSessionIdGenerator();
    }

    /**
     * Configure RedisSessionDAO
     * @Attention Using the shiro-redis open source plug-in
     * @Author Sans
     * @CreateTime 2019/6/12 13:44
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        redisSessionDAO.setKeyPrefix(SESSION_KEY);
        redisSessionDAO.setExpire(expire);
        return redisSessionDAO;
    }

    /**
     * Configure Session Manager
     * @Author Sans
     * @CreateTime 2019/6/12 14:25
     */
    @Bean
    public SessionManager sessionManager() {
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        shiroSessionManager.setSessionDAO(redisSessionDAO());
        return shiroSessionManager;
    }
}

4. Implement Rights Control

Shiro can use code or annotations to control permissions. Usually we use annotation control, which is not only simple and convenient, but also more flexible. There are five Shiro annotations:

In general, we do privilege control in our projects. RequiresPermissions and RequiresRoles are used the most, allowing multiple roles and privileges. The default logic is AND, which means having both of these methods to access them. You can set them as parameters in your comments to OR

Example

 

Use order: Shiro annotations are sequential, and when multiple annotations are on one method, they are checked one by one until they all pass. The default intercept order is RequiresRoles->RequiresPermissions->RequiresAuthentication->
RequiresUser->RequiresGuest

Example

Create UserRoleController Role Interception Test Class

/**
 * @Description Role testing
 * @Author Sans
 * @CreateTime 2019/6/19 11:38
 */
@RestController
@RequestMapping("/role")
public class UserRoleController {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    /**
     * Administrator Role Test Interface
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getAdminInfo")
    @RequiresRoles("ADMIN")
    public Map<String,Object> getAdminInfo(){
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","Here is the interface that only the Administrator role can access");
        return map;
    }

    /**
     * User Role Test Interface
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getUserInfo")
    @RequiresRoles("USER")
    public Map<String,Object> getUserInfo(){
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","Here is the interface that only user roles can access");
        return map;
    }

    /**
     * Role Test Interface
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getRoleInfo")
    @RequiresRoles(value={"ADMIN","USER"},logical = Logical.OR)
    @RequiresUser
    public Map<String,Object> getRoleInfo(){
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","As long as there are ADMIN perhaps USER Role-accessible interfaces");
        return map;
    }

    /**
     * Logout (Test Logout)
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getLogout")
    @RequiresUser
    public Map<String,Object> getLogout(){
        ShiroUtils.logout();
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","Logout");
        return map;
    }
}

Create UserMenuController Privilege Interception Test Class

/**
 * @Description Permission Test
 * @Author Sans
 * @CreateTime 2019/6/19 11:38
 */
@RestController
@RequestMapping("/menu")
public class UserMenuController {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    /**
     * Get a collection of user information
     * @Author Sans
     * @CreateTime 2019/6/19 10:36
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getUserInfoList")
    @RequiresPermissions("sys:user:info")
    public Map<String,Object> getUserInfoList(){
        Map<String,Object> map = new HashMap<>();
        List<SysUserEntity> sysUserEntityList = sysUserService.list();
        map.put("sysUserEntityList",sysUserEntityList);
        return map;
    }

    /**
     * Get Role Information Collection
     * @Author Sans
     * @CreateTime 2019/6/19 10:37
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getRoleInfoList")
    @RequiresPermissions("sys:role:info")
    public Map<String,Object> getRoleInfoList(){
        Map<String,Object> map = new HashMap<>();
        List<SysRoleEntity> sysRoleEntityList = sysRoleService.list();
        map.put("sysRoleEntityList",sysRoleEntityList);
        return map;
    }

    /**
     * Get permission information collection
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getMenuInfoList")
    @RequiresPermissions("sys:menu:info")
    public Map<String,Object> getMenuInfoList(){
        Map<String,Object> map = new HashMap<>();
        List<SysMenuEntity> sysMenuEntityList = sysMenuService.list();
        map.put("sysMenuEntityList",sysMenuEntityList);
        return map;
    }

    /**
     * Get all the data
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/getInfoAll")
    @RequiresPermissions("sys:info:all")
    public Map<String,Object> getInfoAll(){
        Map<String,Object> map = new HashMap<>();
        List<SysUserEntity> sysUserEntityList = sysUserService.list();
        map.put("sysUserEntityList",sysUserEntityList);
        List<SysRoleEntity> sysRoleEntityList = sysRoleService.list();
        map.put("sysRoleEntityList",sysRoleEntityList);
        List<SysMenuEntity> sysMenuEntityList = sysMenuService.list();
        map.put("sysMenuEntityList",sysMenuEntityList);
        return map;
    }

    /**
     * Add Administrator Role Permissions (test dynamic permission updates)
     * @Author Sans
     * @CreateTime 2019/6/19 10:39
     * @Param  username User ID
     * @Return Map<String,Object> Return results
     */
    @RequestMapping("/addMenu")
    public Map<String,Object> addMenu(){
        //Add Administrator Role Permissions
        SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity();
        sysRoleMenuEntity.setMenuId(4L);
        sysRoleMenuEntity.setRoleId(1L);
        sysRoleMenuService.save(sysRoleMenuEntity);
        //Clear Cache
        String username = "admin";
        ShiroUtils.deleteCache(username,false);
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","Permissions added successfully");
        return map;
    }
}

Create UserLoginController Login Class

/**
 * @Description User Login
 * @Author Sans
 * @CreateTime 2019/6/17 15:21
 */
@RestController
@RequestMapping("/userLogin")
public class UserLoginController {

    @Autowired
    private SysUserService sysUserService;

    /**
     * Sign in
     * @Author Sans
     * @CreateTime 2019/6/20 9:21
     */
    @RequestMapping("/login")
    public Map<String,Object> login(@RequestBody SysUserEntity sysUserEntity){
        Map<String,Object> map = new HashMap<>();
        //Authenticate
        try{
            //Authentication and login
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(sysUserEntity.getUsername(), sysUserEntity.getPassword());
            //Verify successful login operation
            subject.login(token);
        }catch (IncorrectCredentialsException e) {
            map.put("code",500);
            map.put("msg","User does not exist or password error");
            return map;
        } catch (LockedAccountException e) {
            map.put("code",500);
            map.put("msg","Logon failed, the user was frozen");
            return map;
        } catch (AuthenticationException e) {
            map.put("code",500);
            map.put("msg","The user does not exist");
            return map;
        } catch (Exception e) {
            map.put("code",500);
            map.put("msg","Unknown exception");
            return map;
        }
        map.put("code",0);
        map.put("msg","Login Successful");
        map.put("token",ShiroUtils.getSession().getId().toString());
        return map;
    }
    /**
     * Not logged in
     * @Author Sans
     * @CreateTime 2019/6/20 9:22
     */
    @RequestMapping("/unauth")
    public Map<String,Object> unauth(){
        Map<String,Object> map = new HashMap<>();
        map.put("code",500);
        map.put("msg","Not logged in");
        return map;
    }
}

V. POSTMAN testing

After successful login, TOKEN will be returned, because it is a single sign-on, new TOKEN will be returned if you login again, and the TOKEN of Redis will be invalidated before

When we access the interface for the first time, we can see that the cache already has permission data. When we access the interface for the second time, Shiro will go directly to the cache to get permissions, and pay attention to setting the request header when accessing the interface.

ADMIN does not currently have sys:info:all, so it cannot access the getInfoAll interface. To clear the cache after dynamically assigning permissions, Shiro will re-execute the authorization method when accessing the interface, and then put the permissions and role data into the cache again

Access to add permission test interface, because it is a test, I write the ADMIN of the user with added permission to it, after permission is added, call the tool class to clear the cache, we can see that there is no cache in Redis

Access the getInfoAll interface again, because there is no data in the cache, and Shiro reauthorizes the query to block passage through

6. Project Source

https://gitee.com/liselotte/spring-boot-shiro-demo


https://github.com/xuyulong2017/my-java-demo

 

Recommended reading (click to skip reading)

 

1. SpringBoot Content Aggregation

2. Interview Question Content Aggregation

3. Design Mode Content Aggregation

4. Mybatis Content Aggregation

5. Multithreaded Content Aggregation

Posted by bugz-2849 on Thu, 22 Aug 2019 20:17:44 -0700