13°

SpringBoot 2.x 开发案例之 Shiro 整合 Redis

前言

前段时间做了一个图床的小项目,安全框架使用的是Shiro。为了使用户7x24小时访问,决定把项目由单机升级为集群部署架构。但是安全框架shiro只有单机存储的SessionDao,尽管Shrio有基于Ehcache-rmi的组播/广播实现,然而集群的分布往往是跨网段的,甚至是跨地域的,所以寻求新的方案。

架构

方案

使用 redis 集中存储,实现分布式集群共享用户信息,这里我们采用第三方开源插件crazycake来实现,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
# 数据库索引(默认为0)
redis.database=0
# 服务器地址 变更为自己的
redis.host=127.0.0.1
# 服务器连接端口
redis.port=6379
# 服务器连接密码,如果不设置密码注释掉即可
# redis.password=
# 连接超时时间(毫秒)
redis.timeout=30000

本来crazycake插件已经实现了RedisManager,但是参数不可配,这里我们需要自己重写一下:

/**
 * 重写 RedisManager
 * 博客:https://blog.52itstyle.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();
}

}

参数配置 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权限配置
 * 一定要配置 @Configuration 和 @EnableConfigurationProperties 注解
 * 博客:https://blog.52itstyle.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");
    // 拦截器
    Map&lt;String, String&gt; filterChainDefinitionMap = new LinkedHashMap&lt;&gt;();
    /**
     * 静态文件
     */
    filterChainDefinitionMap.put("/file/**","anon");
    /**
     * 登录注册
     */
    filterChainDefinitionMap.put("/register.shtml","anon");
    filterChainDefinitionMap.put("/login.shtml","anon");
    /**
     * 管理后台
     */
    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实现
 * @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层的实现
 * 原理就是重写 AbstractSessionDAO
 * 有兴趣的小伙伴自行阅读源码
 */
@Bean
public RedisSessionDAO redisSessionDAO() {
    RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    redisSessionDAO.setRedisManager(redisManager());
    return redisSessionDAO;
}

}

小结

是不是很爽,以后重启应用再也不用担心用户投诉了?

本文转载自博客园,原文链接:https://www.cnblogs.com/smallSevens/p/12189334.html

全部评论: 0

    我有话说: