参考资料:
https://github.com/Pramy/shiro-redis-spring-boot-starter
https://github.com/alexxiyang/shiro-redis
Shiro 缓存管理
RedisCache.class
@Slf4j
@Component
public class RedisCache implements Cache<Object, Object> {
@Resource(name = "myRedisTemplate")
private RedisTemplate<Object, Object> client;
private String keyPrefix;
private long ttl;
public RedisCache(RedisCacheProperties redisCacheProperties) {
this.keyPrefix = redisCacheProperties.getKeyPrefix();
this.ttl = redisCacheProperties.getValueCacheExpire();
}
private Object getKey(Object k) {
return this.keyPrefix + (k == null ? "*" : k);
}
@Override
public Object get(Object o) throws CacheException {
return client.opsForValue().get(getKey(o));
}
@Override
public Object put(Object o, Object o2) throws CacheException {
if (ttl >= 0) {
client.opsForValue().set(getKey(o), o2, ttl, TimeUnit.MILLISECONDS);
} else {
client.opsForValue().set(getKey(o), o2);
}
return o2;
}
@Override
public Object remove(Object o) throws CacheException {
Object result = get(o);
client.delete(getKey(o));
return result;
}
@Override
public void clear() throws CacheException {
client.delete(getKey("*"));
}
@Override
public int size() {
return keys().size();
}
@Override
public Set<Object> keys() {
return client.keys(getKey("*"));
}
@Override
public Collection<Object> values() {
return client.opsForValue().multiGet(keys());
}
}
RedisCacheManager.class
@Component("myRedisCacheManager")
public class RedisCacheManager implements CacheManager {
@Resource
private RedisCache client;
@SuppressWarnings("unchecked")
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
return (Cache<K, V>) client;
}
}
Shiro Session管理
RedisSessionDao.class
@Component
public class RedisSessionDao extends AbstractSessionDAO {
private final Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
private String keyPrefix;
private Long ttl;
private RedisTemplate<Object, Object> client;
private Cache<Serializable, Session> caches;
public RedisSessionDao(@Qualifier("myRedisTemplate") RedisTemplate<Object, Object> 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<Session> getActiveSessions() {
List<Session> result = new ArrayList<>();
Set<Object> keys = client.keys(getKey("*"));
if (CollectionUtils.isEmpty(keys)) {
return result;
}
List<Object> 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<Object, Object> 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<String, String> filterChain;
private List<Class<?>> classList;
public Map<String, String> getFilterChain() {
return filterChain;
}
public void setFilterChain(Map<String, String> filterChain) {
this.filterChain = filterChain;
}
}
RedisShiroBeanConfig.class
@Configuration
public class RedisShiroBeanConfig {
@Bean(name = "myRedisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> 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