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

本文涉及的产品
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

目录
相关文章
|
2月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
366 2
|
3月前
|
消息中间件 监控 Java
Apache Kafka 分布式流处理平台技术详解与实践指南
本文档全面介绍 Apache Kafka 分布式流处理平台的核心概念、架构设计和实践应用。作为高吞吐量、低延迟的分布式消息系统,Kafka 已成为现代数据管道和流处理应用的事实标准。本文将深入探讨其生产者-消费者模型、主题分区机制、副本复制、流处理API等核心机制,帮助开发者构建可靠、可扩展的实时数据流处理系统。
346 4
|
8月前
|
XML 前端开发 Java
SpringBoot实现文件上传下载功能
本文介绍了如何使用SpringBoot实现文件上传与下载功能,涵盖配置和代码实现。包括Maven依赖配置(如`spring-boot-starter-web`和`spring-boot-starter-thymeleaf`)、前端HTML页面设计、WebConfig路径映射配置、YAML文件路径设置,以及核心的文件上传(通过`MultipartFile`处理)和下载(利用`ResponseEntity`返回文件流)功能的Java代码实现。文章由Colorful_WP撰写,内容详实,适合开发者学习参考。
755 0
|
5月前
|
缓存 前端开发 Java
SpringBoot 实现动态菜单功能完整指南
本文介绍了一个动态菜单系统的实现方案,涵盖数据库设计、SpringBoot后端实现、Vue前端展示及权限控制等内容,适用于中后台系统的权限管理。
439 1
|
7月前
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。
|
6月前
|
监控 安全 Java
Java 开发中基于 Spring Boot 3.2 框架集成 MQTT 5.0 协议实现消息推送与订阅功能的技术方案解析
本文介绍基于Spring Boot 3.2集成MQTT 5.0的消息推送与订阅技术方案,涵盖核心技术栈选型(Spring Boot、Eclipse Paho、HiveMQ)、项目搭建与配置、消息发布与订阅服务实现,以及在智能家居控制系统中的应用实例。同时,详细探讨了安全增强(TLS/SSL)、性能优化(异步处理与背压控制)、测试监控及生产环境部署方案,为构建高可用、高性能的消息通信系统提供全面指导。附资源下载链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
963 0
|
8月前
|
SQL 前端开发 Java
深入理解 Spring Boot 项目中的分页与排序功能
本文深入讲解了在Spring Boot项目中实现分页与排序功能的完整流程。通过实际案例,从Service层接口设计到Mapper层SQL动态生成,再到Controller层参数传递及前端页面交互,逐一剖析每个环节的核心逻辑与实现细节。重点包括分页计算、排序参数校验、动态SQL处理以及前后端联动,确保数据展示高效且安全。适合希望掌握分页排序实现原理的开发者参考学习。
499 4
|
缓存 NoSQL Java
为什么分布式一定要有redis?
1、为什么使用redis 分析:博主觉得在项目中使用redis,主要是从两个角度去考虑:性能和并发。当然,redis还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件(如zookpeer等)代替,并不是非要使用redis。
1492 0
|
机器学习/深度学习 缓存 NoSQL

热门文章

最新文章

推荐镜像

更多