Shiro integration Redis of SpringBoot 2.x development case

Preface

A few days ago, I did a small project of drawing beds. Shiro was used as the security framework. In order to make users access 7x24 hours, it is decided to upgrade the project from stand-alone to cluster deployment architecture. However, the security framework Shiro only has session Dao of stand-alone storage. Although Shrio has a multicast / broadcast implementation based on ehcache RMI, the distribution of clusters is often cross network segments or even cross regions, so new solutions are sought.

Framework

programme

redis is used for centralized storage to realize distributed cluster sharing of user information. Here we use the third-party open-source plug-in, crazycake, to achieve this. pom.xml is introduced:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.2.3</version>
</dependency>

To configure application.properties:

# Redis
# Database index (default is 0)
redis.database=0
# Server address changed to its own
redis.host=127.0.0.1
# Server connection port
redis.port=6379
# Server connection password. If you don't set the password, comment it out
# redis.password=
# Connection timeout (MS)
redis.timeout=30000

Originally, the crazy make plug-in has implemented RedisManager, but the parameters are not matched. Here we need to rewrite it ourselves:

/**
 * Rewrite RedisManager
 * Blog: https://blog.52itstype.vip
 */
public class RedisManager extends WorkAloneRedisManager implements IRedisManager {

    private RedisProperties redis;

    private JedisPool jedisPool;

    public RedisManager(RedisProperties redis) {
        this.redis = redis;
    }

    private void init() {
        synchronized(this) {
            if (this.jedisPool == null) {
                this.jedisPool = new JedisPool(this.getJedisPoolConfig(), redis.getHost(), redis.getPort(),
                        redis.getTimeout(), redis.getPassword(), redis.getDatabase());
            }
        }
    }

    @Override
    protected Jedis getJedis() {
        if (this.jedisPool == null) {
            this.init();
        }
        return this.jedisPool.getResource();
    }
}

Parameter configuration RedisProperties:

@Data
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {

    private String host;
    private int port;
    private int timeout;
    private String password;
    private int database;
}

To configure ShiroConfig:

/**
 * Shiro Permission configuration
 * Be sure to configure the @ Configuration and @ EnableConfigurationProperties annotations
 * Blog: https://blog.52itstype.vip
 */
@Configuration
@EnableConfigurationProperties({RedisProperties.class})
public class ShiroConfig {

    private RedisProperties redis;

    public ShiroConfig(RedisProperties redis) {
        this.redis = redis;
    }

    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean (SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/index.html");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        // Interceptor
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        /**
         * Static file
         */
        filterChainDefinitionMap.put("/file/**","anon");
        /**
         * Login registration
         */
        filterChainDefinitionMap.put("/register.shtml","anon");
        filterChainDefinitionMap.put("/login.shtml","anon");
        /**
         * Management backstage
         */
        filterChainDefinitionMap.put("/sys/**", "roles[admin]");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SessionsSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        securityManager.setCacheManager(cacheManager());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    /**
     * cacheManager Cache redis implementation
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * Configure shiro redisManager
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager(redis);
        return redisManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao Layer implementation
     * The principle is to rewrite AbstractSessionDAO
     * Interested partners read the source code by themselves
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }
}

Summary

Isn't it cool to restart the app in the future without worrying about user complaints?

Finally, the following small yellow chart is recommended: https://www.cloudbed.vip

Tags: Java Redis Shiro Database Session

Posted on Mon, 13 Jan 2020 06:06:06 -0800 by holstead