Shiro's Session caches are mainly two schemes, one is using Session Shiro own, without using the HttpSession, Shiro's own implementation and Session caching Cache interface; the other is the direct use of spring boot spring-session-data-redis
package and configured and RedisTemplate Redis sequence of the method can be used. The second method is relatively simple, first needs a lot of development work. Here is the main idea of the first way.
Shiro cache
To cache Session, the best first integrated Shiro cache. Shiro offers a similar Spring's Cache abstract, that Shiro itself does not implement Cache, but Cache conducted and abstract, facilitate the replacement of different underlying Cache implementation. Shiro provides CacheManager Cache interface and interfaces, as well as interfaces to inject CacheManagerAware CacheManager.
- Realization of Cache cache interface, shiro's Spring Cache Cache to be packed
@SuppressWarnings("unchecked")
class SpringCacheWrapper <V> implements Cache<String, V> {
private final String REDIS_SHIRO_CACHE = "shiro-cache#";
private org.springframework.cache.Cache springCache;
private CacheManager cacheManager;
SpringCacheWrapper(CacheManager cacheManager, @NotNull org.springframework.cache.Cache springCache) {
this.springCache = springCache;
this.cacheManager = cacheManager;
}
@Override
public V get(String key) throws CacheException {
ValueWrapper cacheValue = springCache.get(getCacheKey(key));
return (V) Optional.ofNullable(cacheValue).map(p->p.get()).orElse(null);
}
@Override
public V put(String key, V value) throws CacheException {
springCache.put(getCacheKey(key), value);
return value;
}
@Override
public V remove(String key) throws CacheException {
springCache.evict(getCacheKey(key));
return null;
}
private String getCacheKey(String key) {
return REDIS_SHIRO_CACHE + key;
}
@Override
public void clear() throws CacheException {
springCache.clear();
}
@Override
public int size() {
throw new UnsupportedOperationException("invoke spring cache size method not supported");
}
@Override
public Set<String> () {
throw new UnsupportedOperationException("invoke spring cache keys method not supported");
}
@Override
public Collection<V> values() {
throw new UnsupportedOperationException("invoke spring cache values method not supported");
}
}
CacheManager achieve the cache interface, shiro of CacheManager packaging conduct of Spring CacheManager
public class SpringCacheManagerWrapper implements CacheManager {
private org.springframework.cache.CacheManager cacheManager;
public SpringCacheManagerWrapper(org.springframework.cache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
org.springframework.cache.Cache springCache = cacheManager.getCache(name);
return new SpringCacheWrapper(springCache);
}
}
Session cache
The need to achieve CacheSessionDAO interface, Session caching method.
public class ShiroSessionDAO extends CachingSessionDAO {
private Cache<String, Session> cache;
public ShiroSessionDAO(CacheManager cacheManager) {
String cacheName = getActiveSessionsCacheName();
this.setCacheManager(cacheManager);
this.cache = getCacheManager().getCache(cacheName);
}
@Override
protected void doUpdate(Session session) {
if(session==null) {
return;
}
cache.put(session.getId().toString(), session);
}
@Override
protected void doDelete(Session session) {
if(session==null){
return;
}
cache.remove(session.getId().toString());
}
@Override
protected Serializable doCreate(Session session) {
if(session == null) {
return null;
}
Serializable sessionId = this.generateSessionId(session);
assignSessionId(session, sessionId);
cache.put(sessionId.toString(), session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if(sessionId==null) {
return null;
}
Session session=(Session) cache.get(sessionId.toString());
return session;
}
}
Configuration Bean
Configuration Redis
@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append("#" + method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Bean
public RedisCacheManager redisCacheManager(@Autowired RedisTemplate redisTemplate) {
// spring cache注解序列化配置
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))
.disableCachingNullValues()
.entryTtl(Duration.ofSeconds(60));
Set<String> cacheNames = new HashSet<>();
cacheNames.add("user");
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("user", redisCacheConfiguration.entryTtl(Duration.ofSeconds(120)));
RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
.cacheDefaults(redisCacheConfiguration).transactionAware().initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap).build();
return redisCacheManager;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer); // key序列化
redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<Object>(Object.class);
redisTemplate.setValueSerializer(fastJsonRedisSerializer); // value序列化
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); // Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
Shiro cache configuration and CacheSessionDao
@Bean(name = "securityManager")
public org.apache.shiro.mgt.SecurityManager defaultWebSecurityManager(@Autowired UserRealm userRealm, @Autowired TokenRealm tokenValidateRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setAuthenticator(multiRealmAuthenticator());
securityManager.setRealms(Arrays.asList(userRealm, tokenValidateRealm));
securityManager.setRememberMeManager(rememberMeManager());
securityManager.setCacheManager(new SpringCacheManagerWrapper());
//必须使用DefaultWebSessionManager,不能是ServletContainerSessionManager
DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager();
webSessionManager.setSessionDAO(cachingSessionDAO);
securityManager.setSessionManager(webSessionManager);
return securityManager;
}
to sum up
It can be seen from the above configuration Shiro Session caching step, it is quite troublesome. In this manner would have been intended, there was later found in the online Session Redis integrated package, as shown below, also found in this manner is simpler to use, then directly use Spring Session, and only need to configure Redis (as shown in FIG. ) on it.
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>