SpringBoot 2.x 開(kāi)發(fā)案例之 Shiro 整合 Redis

image

前言

前段時(shí)間做了一個(gè)圖床的小項(xiàng)目,安全框架使用的是Shiro。為了使用戶(hù)7x24小時(shí)訪問(wèn),決定把項(xiàng)目由單機(jī)升級(jí)為集群部署架構(gòu)。但是安全框架shiro只有單機(jī)存儲(chǔ)的SessionDao,盡管Shrio有基于Ehcache-rmi的組播/廣播實(shí)現(xiàn),然而集群的分布往往是跨網(wǎng)段的,甚至是跨地域的,所以尋求新的方案。

架構(gòu)

image

方案

使用 redis 集中存儲(chǔ),實(shí)現(xiàn)分布式集群共享用戶(hù)信息,這里我們采用第三方開(kāi)源插件crazycake來(lái)實(shí)現(xiàn),pom.xml 引入:

<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>

配置 application.properties

# Redis
# 數(shù)據(jù)庫(kù)索引(默認(rèn)為0)
redis.database=0
# 服務(wù)器地址 變更為自己的
redis.host=127.0.0.1
# 服務(wù)器連接端口
redis.port=6379
# 服務(wù)器連接密碼,如果不設(shè)置密碼注釋掉即可
# redis.password=
# 連接超時(shí)時(shí)間(毫秒)
redis.timeout=30000

本來(lái)crazycake插件已經(jīng)實(shí)現(xiàn)了RedisManager,但是參數(shù)不可配,這里我們需要自己重寫(xiě)一下:

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();
    }
}

參數(shù)配置 RedisProperties

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

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

配置 ShiroConfig

/**
 * Shiro權(quán)限配置
 * 一定要配置 @Configuration 和 @EnableConfigurationProperties 注解
 */
@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");
        // 攔截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        /**
         * 靜態(tài)文件
         */
        filterChainDefinitionMap.put("/file/**","anon");
        /**
         * 登錄注冊(cè)
         */
        filterChainDefinitionMap.put("/register.shtml","anon");
        filterChainDefinitionMap.put("/login.shtml","anon");
        /**
         * 管理后臺(tái)
         */
        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 緩存 redis實(shí)現(xiàn)
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

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

    /**
     * RedisSessionDAO shiro sessionDao層的實(shí)現(xiàn)
     * 原理就是重寫(xiě) AbstractSessionDAO
     * 有興趣的小伙伴自行閱讀源碼
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }
}

小結(jié)

是不是很爽,以后重啟應(yīng)用再也不用擔(dān)心用戶(hù)投訴了?

源碼

https://gitee.com/52itstyle/SPTools

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容