Shiro中使用Redis管理session
实现RedisSessionDao思路
Shiro提供了SessionDAO
接口,可以实现此类来操作session,其中提供了
create
新建一个session,并保存到数据库、文件系统或者持久化缓存中。readSession
根据sessionId检索sessionupdate
更新session信息delete
从EIS中删除sessiongetActiveSessions
返回EIS中active状态的session
EIS(Enterprise Information System)企业信息系统,例:文件系统、数据库、企业云等。
最终的继承关系如图所示。
其中CachingSessionDAO
实现了CacheManagerAware
接口,用于标记需要与缓存管理器进行交互的组件或服务。
需要调用setCacheManager
设置一个CacheManager
。
RedisTemplate操作Redis
引入Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加RedisTemplate Bean
@Bean("sessionRedisTemplate")
public RedisTemplate<String, Object> sessionRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 在redis-cli中使用keys *列举出来的key不会携带\x00\x05sr\x00这种序列化的信息,是可读的
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(genericJackson2JsonRedisSerializer);
template.setHashKeySerializer(genericJackson2JsonRedisSerializer);
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
template.setDefaultSerializer(genericJackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
RedisTemplate中有一个boundValueOps方法,主要用于操作 Redis 的字符串。
传入一个key值会返回一个BoundValueOperations
,其中主要提供了set
和get
方法来设置和获取对应的信息。
还提供了boundZSetOps
、boundStreamOps
和boundListOps
等方法。
也可以使用opsForSet
和opsForStream
这种来进行数据操作。
使用RedisTemplate实现RedisSessionDao
public class RedisSessionDao extends CachingSessionDAO {
private static final long TIMEOUT = 30;
private static final String SESSION_KEY_FORMAT = "xxx_session_%s";
private final RedisTemplate<String, Object> sessionRedisTemplate;
public RedisSessionDao(@Qualifier("sessionRedisTemplate") RedisTemplate<String, Object> sessionRedisTemplate) {
this.sessionRedisTemplate = sessionRedisTemplate;
setCacheManager(new AbstractCacheManager() {
@Override
protected Cache<Serializable, Session> createCache(String name) throws CacheException {
return new MapCache<Serializable, Session>(name, new ConcurrentHashMap<Serializable, Session>());
}
});
}
@Override
protected void doUpdate(Session session) {
BoundValueOperations<String, Object> sessionValueOperations = sessionRedisTemplate
.boundValueOps(sessionKeyGenerator(session.getId().toString()));
sessionValueOperations.set(session);
sessionValueOperations.expire(TIMEOUT, TimeUnit.MINUTES);
}
@Override
protected void doDelete(Session session) {
sessionRedisTemplate.delete(sessionKeyGenerator(session.getId().toString()));
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
BoundValueOperations<String, Object> stringObjectBoundValueOperations = sessionRedisTemplate.
boundValueOps(sessionKeyGenerator(session.getId().toString()));
stringObjectBoundValueOperations.set(session, TIMEOUT, TimeUnit.MINUTES);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
String sessionKey = sessionKeyGenerator(sessionId.toString());
BoundValueOperations<String, Object> sessionValue = sessionRedisTemplate.boundValueOps(sessionKey);
Session session = (Session) sessionValue.get();
return session;
}
private String sessionKeyGenerator(String sessionId) {
return String.format(SESSION_KEY_FORMAT, sessionId);
}
}
添加ShiroConfig
@Configuration
public class ShiroConfig {
private static final long GLOBAL_SESSION_TIMEOUT = 1000 * 60 * 60 * 24;
/**
* The bean for Security manager.
*
* @param realm the specific realm.
* @return Security manager.
*/
@Bean
public SecurityManager securityManager(Realm realm, @Qualifier("CustomSessionManager") SessionManager sessionManager) {
val securityManager = new DefaultWebSecurityManager(realm);
securityManager.setSessionManager(sessionManager);
SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
@Bean("CustomSessionManager")
public SessionManager sessionManager(RedisSessionDao sessionDao) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(sessionDao);
sessionManager.setGlobalSessionTimeout(GLOBAL_SESSION_TIMEOUT);
return sessionManager;
}
/**
* The bean for security manager.
*
* @param securityManager security manager.
* @return Shiro filter factory bean.
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(
SecurityManager securityManager) {
val shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
val filters = shiroFilterFactoryBean.getFilters();
filters.put("authc", new CustomFormAuthenticationFilter());
val shiroFilterDefinitionMap = new LinkedHashMap<String, String>();
shiroFilterDefinitionMap.put("/v1.0/session", "anon");
shiroFilterDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroFilterDefinitionMap);
return shiroFilterFactoryBean;
}
}
Docker启动Redis
docker run -p 6379:6379 --name redis -d redis --appendonly yes --requirepass "your_password"
使用到的Redis的一些命令
docker exec -it your-container redis-cli
- 如果设置了密码需要使用
auth your_password
进行验证 keys *
获取所有keyget key_name
获取key_name对应的信息ttl key_name
查看key_name的过期时间del key_name
删除key_nameflushall
清除所有保存的信息set key_name xxx
设置key_name的值为xxx