Shiro使用Session缓存

Shiro的Session缓存主要有两种方案,一种是使用Shiro自己的Session,不使用HttpSession,自己实现Shiro的Cache接口和Session缓存等;另外一种是直接使用spring boot的spring-session-data-redis的包,并且配置RedisTemplate和Redis的序列化方法就可以了。相对来说第二种方式非常简单,第一种还需要不少开发工作。下面主要来说第一种方式的思路。

Shiro的缓存

要缓存Session,最好要先集成Shiro的缓存。Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现。 Shiro提供了Cache接口和CacheManager接口,以及CacheManagerAware接口来注入CacheManager。

  • 实现Cache的缓存接口,shiro的Cache对进行Spring Cache包装
@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的缓存接口,shiro的CacheManager对进行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的缓存

需要实现CacheSessionDAO接口,实现Session的缓存方法。

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;
    }
}

配置Bean

配置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的缓存和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;
}

总结

从上面的步骤可以看出配置Shiro的Session缓存,还是比较麻烦的。本来也是打算采用这种方式,后来在网上发现有个Session集成Redis的包,如下所示,也发现使用这种方式更简单,后来就直接使用Spring的Session了,只需要配置Redis(如上所示)就可以了。

 <dependency>
     <groupId>org.springframework.session</groupId>
     <artifactId>spring-session-data-redis</artifactId>
</dependency>

猜你喜欢

转载自www.cnblogs.com/fzsyw/p/11531692.html