我們在上一遍文檔中已經完成了Shiro驗證功能。(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基礎上我們將完成分布式Session共享功能。
Redis的使用
Maven Plugin添加Redis相關jar包
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4 </dependency>
添加Redis配置文件
1 package com.goku.webapi.config.redis; 2 3 import com.fasterxml.jackson.databind.DeserializationFeature; 4 import org.springframework.beans.factory.annotation.Value; 5 import org.springframework.cache.CacheManager; 6 import org.springframework.cache.annotation.CachingConfigurerSupport; 7 import org.springframework.cache.annotation.EnableCaching; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.data.redis.cache.RedisCacheManager; 11 import org.springframework.data.redis.connection.RedisConnectionFactory; 12 import org.springframework.data.redis.core.RedisTemplate; 13 import org.springframework.data.redis.core.StringRedisTemplate; 14 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 15 16 import com.fasterxml.jackson.annotation.JsonAutoDetect; 17 import com.fasterxml.jackson.annotation.PropertyAccessor; 18 import com.fasterxml.jackson.databind.ObjectMapper; 19 import org.springframework.data.redis.serializer.StringRedisSerializer; 20 21 /** 22 * Created by nbfujx on 2017/10/19. 23 */ 24 @Configuration 25 @EnableCaching 26 public class RedisCacheConfig extends CachingConfigurerSupport { 27 28 @Value("${spring.redis.host}") 29 private String host; 30 @Value("${spring.redis.port}") 31 private int port; 32 @Value("${spring.redis.timeout}") 33 private int timeout; 34 35 @Bean 36 public CacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) { 37 RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); 38 cacheManager.setDefaultExpiration(1800); 39 return cacheManager; 40 } 41 42 @Bean 43 public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) { 44 RedisTemplate<Object, Object> template = new RedisTemplate<>(); 45 template.setConnectionFactory(factory); 46 template.setKeySerializer(new StringRedisSerializer()); 47 template.setValueSerializer(new RedisObjectSerializer()); 48 return template; 49 } 50 }
添加RedisSessionDAO配置文件
1 package com.goku.webapi.config.Shiro; 2 3 import org.apache.shiro.session.Session; 4 import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.data.redis.core.RedisTemplate; 8 9 import javax.annotation.Resource; 10 import java.io.Serializable; 11 import java.util.concurrent.TimeUnit; 12 13 /** 14 * Created by nbfujx on 2017-11-08. 15 */ 16 public class RedisSessionDAO extends EnterpriseCacheSessionDAO { 17 18 // session 在redis過期時間是30分鍾30*60 19 private static final int EXPIRE_TIME = 1800; 20 private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class); 21 private static String prefix = "shiro-session:"; 22 23 @Resource 24 private RedisTemplate<String, Object> redisTemplate; 25 26 // 創建session,保存到數據庫 27 @Override 28 protected Serializable doCreate(Session session) { 29 Serializable sessionId = super.doCreate(session); 30 this.logger.info("創建session:{}", session.getId()); 31 redisTemplate.opsForValue().set(prefix + sessionId.toString(), session); 32 return sessionId; 33 } 34 35 // 獲取session 36 @Override 37 protected Session doReadSession(Serializable sessionId) { 38 this.logger.info("獲取session:{}", sessionId); 39 // 先從緩存中獲取session,如果沒有再去數據庫中獲取 40 Session session = super.doReadSession(sessionId); 41 if (session == null) { 42 session = (Session) redisTemplate.opsForValue().get(prefix + sessionId.toString()); 43 } 44 return session; 45 } 46 47 // 更新session的最后一次訪問時間 48 @Override 49 protected void doUpdate(Session session) { 50 super.doUpdate(session); 51 this.logger.info("獲取session:{}", session.getId()); 52 String key = prefix + session.getId().toString(); 53 if (!redisTemplate.hasKey(key)) { 54 redisTemplate.opsForValue().set(key, session); 55 } 56 redisTemplate.expire(key, EXPIRE_TIME, TimeUnit.SECONDS); 57 } 58 59 // 刪除session 60 @Override 61 protected void doDelete(Session session) { 62 this.logger.info("刪除session:{}", session.getId()); 63 super.doDelete(session); 64 redisTemplate.delete(prefix + session.getId().toString()); 65 } 66 }
添加ShiroCache配置文件
1 package com.goku.webapi.config.Shiro; 2 3 import org.apache.shiro.cache.Cache; 4 import org.apache.shiro.cache.CacheException; 5 import org.springframework.data.redis.core.RedisTemplate; 6 7 import java.util.ArrayList; 8 import java.util.Collection; 9 import java.util.List; 10 import java.util.Set; 11 import java.util.concurrent.TimeUnit; 12 13 /** 14 * Created by nbfujx on 2017/11/8. 15 */ 16 public class ShiroCache<K, V> implements Cache<K, V> { 17 18 private static final String REDIS_SHIRO_CACHE = "shiro-cache:"; 19 private static final long GLOB_EXPIRE = 30; 20 private String cacheKey; 21 private RedisTemplate<K, V> redisTemplate; 22 23 public ShiroCache(RedisTemplate<K, V> client, String name) { 24 this.cacheKey = REDIS_SHIRO_CACHE + name + ":"; 25 this.redisTemplate = client; 26 } 27 28 @Override 29 public V get(K key) throws CacheException { 30 redisTemplate.boundValueOps(getCacheKey(key)).expire(GLOB_EXPIRE, TimeUnit.MINUTES); 31 return redisTemplate.boundValueOps(getCacheKey(key)).get(); 32 } 33 34 @Override 35 public V put(K key, V value) throws CacheException { 36 V old = get(key); 37 redisTemplate.boundValueOps(getCacheKey(key)).set(value); 38 return old; 39 } 40 41 @Override 42 public V remove(K key) throws CacheException { 43 V old = get(key); 44 redisTemplate.delete(getCacheKey(key)); 45 return old; 46 } 47 48 @Override 49 public void clear() throws CacheException { 50 redisTemplate.delete(keys()); 51 } 52 53 @Override 54 public int size() { 55 return keys().size(); 56 } 57 58 @Override 59 public Set<K> keys() { 60 return redisTemplate.keys(getCacheKey("*")); 61 } 62 63 @Override 64 public Collection<V> values() { 65 Set<K> set = keys(); 66 List<V> list = new ArrayList<>(); 67 for (K s : set) { 68 list.add(get(s)); 69 } 70 return list; 71 } 72 73 private K getCacheKey(Object k) { 74 return (K) (this.cacheKey + k); 75 } 76 }
添加RedisCacheManager配置文件
1 package com.goku.webapi.config.Shiro; 2 3 import javax.annotation.Resource; 4 5 import com.goku.webapi.config.Shiro.ShiroCache; 6 import org.apache.shiro.cache.AbstractCacheManager; 7 import org.apache.shiro.cache.Cache; 8 import org.apache.shiro.cache.CacheException; 9 import org.springframework.data.redis.core.RedisTemplate; 10 /** 11 * Created by nbfujx on 2017-11-08. 12 */ 13 public class RedisCacheManager extends AbstractCacheManager { 14 15 @Resource 16 private RedisTemplate<String, Object> redisTemplate; 17 18 @Override 19 protected Cache<String, Object> createCache(String name) throws CacheException { 20 return new ShiroCache<>(redisTemplate, name); 21 } 22 }
調整ShiroConfi配置文件
1 package com.goku.webapi.config.Shiro; 2 3 import org.apache.shiro.session.mgt.SessionManager; 4 import org.apache.shiro.spring.LifecycleBeanPostProcessor; 5 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 6 import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 7 import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 8 import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; 9 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; 10 import org.springframework.context.annotation.Bean; 11 import org.springframework.context.annotation.Configuration; 12 import org.springframework.context.annotation.DependsOn; 13 14 import javax.servlet.Filter; 15 import java.util.LinkedHashMap; 16 import java.util.Map; 17 18 19 /** 20 * shiro配置類 21 * Created by nbfujx on 2017/11/7. 22 */ 23 @Configuration 24 public class ShiroConfig { 25 26 /** 27 * LifecycleBeanPostProcessor,這是個DestructionAwareBeanPostProcessor的子類, 28 * 負責org.apache.shiro.util.Initializable類型bean的生命周期的,初始化和銷毀。 29 * 主要是AuthorizingRealm類的子類,以及EhCacheManager類。 30 */ 31 @Bean(name = "lifecycleBeanPostProcessor") 32 public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { 33 return new LifecycleBeanPostProcessor(); 34 } 35 36 37 /** 38 * ShiroRealm,這是個自定義的認證類,繼承自AuthorizingRealm, 39 * 負責用戶的認證和權限的處理,可以參考JdbcRealm的實現。 40 */ 41 @Bean(name = "shiroRealm") 42 @DependsOn("lifecycleBeanPostProcessor") 43 public ShiroRealm shiroRealm() { 44 ShiroRealm realm = new ShiroRealm(); 45 realm.setCacheManager(redisCacheManager()); 46 return realm; 47 } 48 49 @Bean 50 public RedisCacheManager redisCacheManager() { 51 return new RedisCacheManager(); 52 } 53 54 @Bean(name = "redisSessionDAO") 55 public RedisSessionDAO sessionDAO() { 56 RedisSessionDAO sessionDAO = new RedisSessionDAO(); 57 return sessionDAO; 58 } 59 60 /** 61 * SessionManager session管理 62 */ 63 @Bean 64 public SessionManager sessionManager() { 65 DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); 66 sessionManager.setSessionDAO(sessionDAO()); 67 sessionManager.setGlobalSessionTimeout(1800); 68 sessionManager.setCacheManager(redisCacheManager()); 69 return sessionManager; 70 } 71 72 /** 73 * SecurityManager,權限管理,這個類組合了登陸,登出,權限,session的處理,是個比較重要的類。 74 */ 75 @Bean(name = "securityManager") 76 public DefaultWebSecurityManager securityManager() { 77 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 78 securityManager.setRealm(shiroRealm()); 79 securityManager.setCacheManager(redisCacheManager()); 80 securityManager.setSessionManager(sessionManager()); 81 return securityManager; 82 } 83 84 85 /** 86 * ShiroFilterFactoryBean,是個factorybean,為了生成ShiroFilter。 87 * 它主要保持了三項數據,securityManager,filters,filterChainDefinitionManager。 88 */ 89 @Bean(name = "shiroFilter") 90 public ShiroFilterFactoryBean shiroFilterFactoryBean() { 91 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 92 shiroFilterFactoryBean.setSecurityManager(securityManager()); 93 94 Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); 95 shiroFilterFactoryBean.setFilters(filters); 96 97 98 Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>(); 99 filterChainDefinitionManager.put("/login", "anon"); 100 filterChainDefinitionManager.put("/logout", "anon"); 101 filterChainDefinitionManager.put("/sysUser/*", "authc,perms");//"authc,perms[sysUser:*]"); 102 filterChainDefinitionManager.put("/sysMenu/*", "authc,perms");//"authc,perms[sysUser:*]"); 103 filterChainDefinitionManager.put("/*", "anon"); 104 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager); 105 106 shiroFilterFactoryBean.setLoginUrl("/notAuthc"); 107 shiroFilterFactoryBean.setSuccessUrl("/"); 108 shiroFilterFactoryBean.setUnauthorizedUrl("/notAuthz"); 109 return shiroFilterFactoryBean; 110 } 111 112 /** 113 * DefaultAdvisorAutoProxyCreator,Spring的一個bean,由Advisor決定對哪些類的方法進行AOP代理。 114 */ 115 @Bean 116 @DependsOn("lifecycleBeanPostProcessor") 117 public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { 118 DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); 119 defaultAAP.setProxyTargetClass(true); 120 return defaultAAP; 121 } 122 123 /** 124 * AuthorizationAttributeSourceAdvisor,shiro里實現的Advisor類, 125 * 內部使用AopAllianceAnnotationsAuthorizingMethodInterceptor來攔截用以下注解的方法。 126 */ 127 @Bean 128 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { 129 AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor(); 130 aASA.setSecurityManager(securityManager()); 131 return aASA; 132 } 133 134 135 136 137 }
新增RedisCacheManager,RedisSessionDAO,sessionManager相關bean,
securityManager 增加 securityManager.setSessionManager(sessionManager());
Shiro-Redis的使用驗證
先進行登錄驗證操作

查看redis存儲數據

進行數據查詢

查看日志是否從redis獲取

GITHUB
github : https://github.com/nbfujx/learn-java-demo/tree/master/Goku.WebService.Simple.Redis.Shiro
