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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 我们在上一遍文档中已经完成了Shiro验证功能。(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基础上我们将完成分布式Session共享功能。Redis的使用Maven Plugin添加Redis相关jar包1 2 org.

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

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2天前
基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
51 36
|
4天前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
34 11
|
2月前
|
NoSQL Java API
springboot项目Redis统计在线用户
通过本文的介绍,您可以在Spring Boot项目中使用Redis实现在线用户统计。通过合理配置Redis和实现用户登录、注销及统计逻辑,您可以高效地管理在线用户。希望本文的详细解释和代码示例能帮助您在实际项目中成功应用这一技术。
72 4
|
2月前
|
消息中间件 NoSQL Java
Spring Boot整合Redis
通过Spring Boot整合Redis,可以显著提升应用的性能和响应速度。在本文中,我们详细介绍了如何配置和使用Redis,包括基本的CRUD操作和具有过期时间的值设置方法。希望本文能帮助你在实际项目中高效地整合和使用Redis。
96 2
|
3月前
|
安全 Java 数据库
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
这篇文章是关于Apache Shiro权限管理框架的详细学习指南,涵盖了Shiro的基本概念、认证与授权流程,并通过Spring Boot测试模块演示了Shiro在单应用环境下的使用,包括与IniRealm、JdbcRealm的集成以及自定义Realm的实现。
66 3
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
|
3月前
|
存储 缓存 NoSQL
分布式架构下 Session 共享的方案
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求、系统架构和性能要求等因素,选择合适的 Session 共享方案。同时,还需要不断地进行优化和调整,以确保系统的稳定性和可靠性。
|
3月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
201 2
|
3月前
|
JSON NoSQL Java
springBoot:jwt&redis&文件操作&常见请求错误代码&参数注解 (九)
该文档涵盖JWT(JSON Web Token)的组成、依赖、工具类创建及拦截器配置,并介绍了Redis的依赖配置与文件操作相关功能,包括文件上传、下载、删除及批量删除的方法。同时,文档还列举了常见的HTTP请求错误代码及其含义,并详细解释了@RequestParam与@PathVariable等参数注解的区别与用法。
|
3月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
59 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
2月前
|
JavaScript NoSQL Java
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
70 0

推荐镜像

更多