A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小刀葛小伦 于 2020-1-17 13:01 编辑

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

方案
使用 redis 集中存储,实现分布式集群共享用户信息,这里我们采用第三方开源插件crazycake来实现,pom.xml 引入:
[XML] 纯文本查看 复制代码
<dependency>    [/align][align=left]<groupId>org.springframework.boot</groupId>  [/align][align=left]  <artifactId>spring-boot-starter-data-redis</artifactId>[/align][align=left]</dependency>[/align][align=left]<dependency>   [/align][align=left] <groupId>org.crazycake</groupId>  [/align][align=left]  <artifactId>shiro-redis</artifactId> [/align][align=left]   <version>3.2.3</version>[/align][align=left]</dependecy>
配置 application.properties:
[PowerShell] 纯文本查看 复制代码
# Redis# 数据库索引(默认为0)redis.database=0
# 服务器地址 变更为自己的
redis.host=127.0.0.1
# 服务器连接端口
redis.port=6379
# 服务器连接密码,如果不设置密码注释掉即可
# redis.password=
# 连接超时时间(毫秒)
redis.timeout=30000
本来crazycake插件已经实现了RedisManager,但是参数不可配,这里我们需要自己重写一下:
[Java] 纯文本查看 复制代码
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:
[Java] 纯文本查看 复制代码
@Data
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {    
private String host;    
private int port;    
private int timeout;    
private String password;    
private int database;}
配置 ShiroConfig:
[Java] 纯文本查看 复制代码
/** * Shiro权限配置 * 一定要配置 @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<>(); 
       /**
         * 静态文件
         */ 
       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; 
   }}
小结
是不是很爽,以后重启应用再也不用担心用户投诉了?


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马