SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能

  1. 云栖社区>
  2. 博客>
  3. 正文

SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能

nbfujx 2017-11-02 19:09:00 浏览2247
展开阅读全文

我们在上一遍文档中已经完成了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>
View Code

添加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 }
View Code

添加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 }
View Code

添加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 }
View Code

添加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 }
View Code

调整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 }
View Code

新增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

网友评论

登录后评论
0/500
评论
nbfujx
+ 关注