我们在上一遍文档中已经完成了Shiro验证功能。(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基础上我们将完成分布式Session共享功能。
Redis的使用
Maven Plugin添加Redis相关jar包
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4 </dependency>
添加Redis配置文件
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
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配置文件
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
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配置文件
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
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配置文件
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
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配置文件
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
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