zoukankan      html  css  js  c++  java
  • 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>
    
  • 相关阅读:
    spring-boot启动debug信息中non-fatal error解决
    is present but cannot be translated into a null value due to being declared as a primitive type
    mysql联合索引
    Spring RestTemplate 专题
    Android Studio开发入门-引用jar及so文件
    它们偷偷干了啥?教你监督APP的运行
    解决Android中No resource found that matches android:TextAppearance.Material.Widget.Button.Inverse问题
    No resource found that matches the given name 'android:WindowTitle'
    Android studio:library module依赖篇
    com.android.support:appcompat-v7:21.+
  • 原文地址:https://www.cnblogs.com/fzsyw/p/11531692.html
Copyright © 2011-2022 走看看