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

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

目录
相关文章
|
5月前
|
安全 Java 数据库
第16课:Spring Boot中集成 Shiro
第16课:Spring Boot中集成 Shiro
814 0
|
9月前
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
497 0
|
9月前
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
361 0
|
6月前
|
缓存 安全 Java
Shiro简介及SpringBoot集成Shiro(狂神说视频简易版)
Shiro简介及SpringBoot集成Shiro(狂神说视频简易版)
554 7
|
11月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
830 11
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
335 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
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 的前后端分离的后台管理系统
429 0
|
7月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
2月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
3月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
219 1
Redis专题-实战篇二-商户查询缓存

推荐镜像

更多