前言
前段时间做了一个图床的小项目,安全框架使用的是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] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
9
|
# Redis# 数据库索引(默认为0)redis.database=0
# 服务器地址 变更为自己的
redis.host=127.0.0.1
# 服务器连接端口
redis.port=6379
# 服务器连接密码,如果不设置密码注释掉即可
# redis.password=
# 连接超时时间(毫秒)
redis.timeout=30000
|
本来crazycake插件已经实现了RedisManager,但是参数不可配,这里我们需要自己重写一下:
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
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;
}}
|
小结
是不是很爽,以后重启应用再也不用担心用户投诉了?