参考资料:
https://github.com/Pramy/shiro-redis-spring-boot-starter
https://github.com/alexxiyang/shiro-redis
RedisCache.class
@Slf4j
@Component
public class RedisCache implements Cache
RedisCacheManager.class
@Component("myRedisCacheManager")
public class RedisCacheManager implements CacheManager {
@Resource
private RedisCache client;
@SuppressWarnings("unchecked")
@Override
public Cache getCache(String name) throws CacheException {
return (Cache) client;
}
}
RedisSessionDao.class
@Component
public class RedisSessionDao extends AbstractSessionDAO {
private final Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
private String keyPrefix;
private Long ttl;
private RedisTemplate client;
private Cache caches;
public RedisSessionDao(@Qualifier("myRedisTemplate") RedisTemplate redisTemplate,
RedisCacheProperties redisCacheProperties) {
init(redisCacheProperties.getSessionPrefix(), redisCacheProperties.getSessionCacheExpire(), redisTemplate);
}
@Override
protected Serializable doCreate(Session session) {
logger.debug("doCreate");
if (session == null) {
logger.error("session is null");
throw new SessionException("session is null");
}
Serializable id = super.generateSessionId(session);
((SimpleSession) session).setId(id);
saveSession(session);
return id;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
logger.error("session id is null");
throw new SessionException("session id is null");
}
Session session = caches.getIfPresent(sessionId);
if (session == null) {
logger.debug("doReadSession " + sessionId + " from redis");
session = (Session) client.opsForValue().get(getKey(sessionId));
if (session != null) {
caches.put(sessionId, session);
}
}
return session;
}
@Override
public void update(Session session) throws UnknownSessionException {
if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
delete(session);
} else {
logger.debug("update" + session.getId() + " session");
saveSession(session);
}
}
@Override
public void delete(Session session) {
if (session == null || session.getId() == null) {
logger.error("session or session id is null");
throw new UnknownSessionException("session or session id is null");
}
caches.invalidate(session.getId());
logger.debug("delete " + session.getId() + " from redis");
Object key = getKey(session.getId());
client.delete(key);
}
@Override
public Collection getActiveSessions() {
List result = new ArrayList<>();
Set keys = client.keys(getKey("*"));
if (CollectionUtils.isEmpty(keys)) {
return result;
}
List values = client.opsForValue().multiGet(keys);
if (CollectionUtils.isEmpty(values)) {
return result;
}
for (Object o : values) {
result.add((Session) o);
}
return result;
}
private void saveSession(Session session) {
if (session == null || session.getId() == null) {
logger.error("session or session id is null");
throw new UnknownSessionException("session or session id is null");
}
Object key = getKey(session.getId());
logger.debug("save session to redis");
client.opsForValue().set(key, session, ttl, TimeUnit.MILLISECONDS);
caches.put(session.getId(), session);
}
private Object getKey(Object o) {
return keyPrefix + (o == null ? "*" : o);
}
private void init(String keyPrefix, Long ttl, RedisTemplate client) {
this.keyPrefix = keyPrefix;
if (ttl > 0) {
this.ttl = ttl;
} else {
this.ttl = 0L;
logger.debug("session expire must be more than 0");
}
this.client = client;
caches = CacheBuilder.newBuilder()
.initialCapacity(500)
.weakValues()
.maximumSize(1000)
.expireAfterAccess(1, TimeUnit.MINUTES)
.build();
}
}
其他配置类:
RedisCacheProperties.class
@Data
@Component
@ConfigurationProperties(prefix = "spring.shiro.redis")
public class RedisCacheProperties {
public static final long MILLIS_PER_SECOND = 1000;
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
public static final long MILLIS_DAY = 24 * MILLIS_PER_HOUR;
private String keyPrefix = "shiro:cache:";
private String sessionPrefix = "shiro:session:";
/**
* Time unit:millis
*/
private Long sessionTimeOut = 30 * MILLIS_PER_MINUTE;
/**
* Time unit:millis
*/
private Long sessionCacheExpire = MILLIS_DAY;
/**
* Time unit:millis
*/
private Long valueCacheExpire = -1L;
private boolean isSerializeTransient = true;
private Map filterChain;
private List> classList;
public Map getFilterChain() {
return filterChain;
}
public void setFilterChain(Map filterChain) {
this.filterChain = filterChain;
}
}
RedisShiroBeanConfig.class
@Configuration
public class RedisShiroBeanConfig {
@Bean(name = "myRedisTemplate")
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
@Bean(name = "myRedisSessionManager")
public SessionManager sessionManager(RedisSessionDao redisSessionDao,
RedisCacheProperties redisCacheProperties,
RedisCacheManager redisCacheManager) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDao);
long time = redisCacheProperties.getSessionTimeOut();
sessionManager.setGlobalSessionTimeout(time > 0 ? time : RedisCacheProperties.MILLIS_PER_MINUTE * 30);
sessionManager.setCacheManager(redisCacheManager);
return sessionManager;
}
}
最后在ShiroCofnig类中添加
@Bean
public SessionsSecurityManager securityManager(
@Qualifier("myRedisCacheManager") RedisCacheManager redisCacheManager,
@Qualifier("myRedisSessionManager") SessionManager myRedisSessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
securityManager.setCacheManager(redisCacheManager);
// 使用 spring-session-data-redis 实现分布式session管理
securityManager.setSessionManager(myRedisSessionManager);
return securityManager;
}
参考资料:
https://github.com/Pramy/shiro-redis-spring-boot-starter
https://github.com/alexxiyang/shiro-redis