SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能
我们在上一遍文档中已经完成了Shiro验证功能。(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基础上我们将完成分布式Session共享功能。
Redis的使用
Maven Plugin添加Redis相关jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加Redis配置文件
package com.goku.webapi.config.redis; import com.fasterxml.jackson.databind.DeserializationFeature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.serializer.StringRedisSerializer; /**
* Created by nbfujx on 2017/10/19.
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport { @Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout; @Bean
public CacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(1800);
return cacheManager;
} @Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new RedisObjectSerializer());
return template;
}
}
添加RedisSessionDAO配置文件
package com.goku.webapi.config.Shiro; import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate; import javax.annotation.Resource;
import java.io.Serializable;
import java.util.concurrent.TimeUnit; /**
* Created by nbfujx on 2017-11-08.
*/
public class RedisSessionDAO extends EnterpriseCacheSessionDAO { // session 在redis过期时间是30分钟30*60
private static final int EXPIRE_TIME = 1800;
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
private static String prefix = "shiro-session:"; @Resource
private RedisTemplate<String, Object> redisTemplate; // 创建session,保存到数据库
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = super.doCreate(session);
this.logger.info("创建session:{}", session.getId());
redisTemplate.opsForValue().set(prefix + sessionId.toString(), session);
return sessionId;
} // 获取session
@Override
protected Session doReadSession(Serializable sessionId) {
this.logger.info("获取session:{}", sessionId);
// 先从缓存中获取session,如果没有再去数据库中获取
Session session = super.doReadSession(sessionId);
if (session == null) {
session = (Session) redisTemplate.opsForValue().get(prefix + sessionId.toString());
}
return session;
} // 更新session的最后一次访问时间
@Override
protected void doUpdate(Session session) {
super.doUpdate(session);
this.logger.info("获取session:{}", session.getId());
String key = prefix + session.getId().toString();
if (!redisTemplate.hasKey(key)) {
redisTemplate.opsForValue().set(key, session);
}
redisTemplate.expire(key, EXPIRE_TIME, TimeUnit.SECONDS);
} // 删除session
@Override
protected void doDelete(Session session) {
this.logger.info("删除session:{}", session.getId());
super.doDelete(session);
redisTemplate.delete(prefix + session.getId().toString());
}
}
添加ShiroCache配置文件
package com.goku.webapi.config.Shiro; import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.data.redis.core.RedisTemplate; import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit; /**
* Created by nbfujx on 2017/11/8.
*/
public class ShiroCache<K, V> implements Cache<K, V> { private static final String REDIS_SHIRO_CACHE = "shiro-cache:";
private static final long GLOB_EXPIRE = 30;
private String cacheKey;
private RedisTemplate<K, V> redisTemplate; public ShiroCache(RedisTemplate<K, V> client, String name) {
this.cacheKey = REDIS_SHIRO_CACHE + name + ":";
this.redisTemplate = client;
} @Override
public V get(K key) throws CacheException {
redisTemplate.boundValueOps(getCacheKey(key)).expire(GLOB_EXPIRE, TimeUnit.MINUTES);
return redisTemplate.boundValueOps(getCacheKey(key)).get();
} @Override
public V put(K key, V value) throws CacheException {
V old = get(key);
redisTemplate.boundValueOps(getCacheKey(key)).set(value);
return old;
} @Override
public V remove(K key) throws CacheException {
V old = get(key);
redisTemplate.delete(getCacheKey(key));
return old;
} @Override
public void clear() throws CacheException {
redisTemplate.delete(keys());
} @Override
public int size() {
return keys().size();
} @Override
public Set<K> keys() {
return redisTemplate.keys(getCacheKey("*"));
} @Override
public Collection<V> values() {
Set<K> set = keys();
List<V> list = new ArrayList<>();
for (K s : set) {
list.add(get(s));
}
return list;
} private K getCacheKey(Object k) {
return (K) (this.cacheKey + k);
}
}
添加RedisCacheManager配置文件
package com.goku.webapi.config.Shiro; import javax.annotation.Resource; import com.goku.webapi.config.Shiro.ShiroCache;
import org.apache.shiro.cache.AbstractCacheManager;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Created by nbfujx on 2017-11-08.
*/
public class RedisCacheManager extends AbstractCacheManager { @Resource
private RedisTemplate<String, Object> redisTemplate; @Override
protected Cache<String, Object> createCache(String name) throws CacheException {
return new ShiroCache<>(redisTemplate, name);
}
}
调整ShiroConfi配置文件
package com.goku.webapi.config.Shiro; import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map; /**
* shiro配置类
* Created by nbfujx on 2017/11/7.
*/
@Configuration
public class ShiroConfig { /**
* LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,
* 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
* 主要是AuthorizingRealm类的子类,以及EhCacheManager类。
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} /**
* ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,
* 负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
*/
@Bean(name = "shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroRealm shiroRealm() {
ShiroRealm realm = new ShiroRealm();
realm.setCacheManager(redisCacheManager());
return realm;
} @Bean
public RedisCacheManager redisCacheManager() {
return new RedisCacheManager();
} @Bean(name = "redisSessionDAO")
public RedisSessionDAO sessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
return sessionDAO;
} /**
* SessionManager session管理
*/
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(sessionDAO());
sessionManager.setGlobalSessionTimeout(1800);
sessionManager.setCacheManager(redisCacheManager());
return sessionManager;
} /**
* SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
securityManager.setCacheManager(redisCacheManager());
securityManager.setSessionManager(sessionManager());
return securityManager;
} /**
* ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。
* 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager()); Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
shiroFilterFactoryBean.setFilters(filters); Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
filterChainDefinitionManager.put("/login", "anon");
filterChainDefinitionManager.put("/logout", "anon");
filterChainDefinitionManager.put("/sysUser/*", "authc,perms");//"authc,perms[sysUser:*]");
filterChainDefinitionManager.put("/sysMenu/*", "authc,perms");//"authc,perms[sysUser:*]");
filterChainDefinitionManager.put("/*", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager); shiroFilterFactoryBean.setLoginUrl("/notAuthc");
shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.setUnauthorizedUrl("/notAuthz");
return shiroFilterFactoryBean;
} /**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
} /**
* AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,
* 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
aASA.setSecurityManager(securityManager());
return aASA;
} }
新增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
SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能的更多相关文章
- SpringBoot搭建基于Apache Shiro的权限管理功能
Shiro 是什么 Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户“登录”: 授权 - 访问控制: 密码加密 ...
- Spring Session + Redis实现分布式Session共享
发表于 2016-09-29 文章目录 1. Maven依赖 2. 配置Filter 3. Spring配置文件 4. 解决Redis云服务Unable to configure Redis to k ...
- 基于Spring Boot/Spring Session/Redis的分布式Session共享解决方案
分布式Web网站一般都会碰到集群session共享问题,之前也做过一些Spring3的项目,当时解决这个问题做过两种方案,一是利用nginx,session交给nginx控制,但是这个需要额外工作较多 ...
- springboot+redis实现分布式session共享
官方文档,它是spring session项目的redis相关的一个子文档:https://docs.spring.io/spring-session/docs/2.0.0.BUILD-SNAPSHO ...
- 使用SpringSession和Redis解决分布式Session共享问题
SpringSession优势 遵循servlet规范,同样方式获取session,对应用代码无侵入且对于developers透明化 关键点在于做到透明和兼容 接口适配:仍然使用HttpServlet ...
- Spring boot整合redis实现shiro的分布式session共享
我们知道,shiro是通过SessionManager来管理Session的,而对于Session的操作则是通过SessionDao来实现的,默认的情况下,shiro实现了两种SessionDao,分 ...
- SpringBoot 搭建基于 MinIO 的高性能存储服务
1.什么是MinIO MinIO是根据GNU Affero通用公共许可证v3.0发布的高性能对象存储.它与Amazon S3云存储服务兼容.使用MinIO构建用于机器学习,分析和应用程序数据工作负载的 ...
- Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架
Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session import tornado.ioloop import tornado.web from myhas ...
- Linux搭建基于Apache的HTTP服务器
Linux搭建基于Apache的HTTP服务器 实验目标: 通过本实验掌握基于Linux的WWW服务器搭建. 实验步骤: 1.安装http服务 2.防火墙放通http服务 3.编辑测试网页 4.开 ...
随机推荐
- SQL Server 2005 的动态管理视图DMV和函数DMF
优化 的动态管理视图DMV和函数DMF SQL Server 05提供了动态管理视图Dynamic Management Views和函数 Functions,方便了我们对系统运行情况的监控,故障诊断 ...
- Android开发新手常见的10个误区
在过去十年中最流行的移动应用开发开发平台中,我们认为,Android平台是一个新开发的最方便的平台.一个廉价的工具,友好的开发者社区,众所周知的编程语言(Java),使得开发Android应用程序从未 ...
- php strcasecmp()函数 语法
php strcasecmp()函数 语法 作用:比较两个字符串(不区分大小写)直线电机驱动器 语法:strcasecmp(string1,string2) 参数: 参数 描述 string1 必须, ...
- LYOI2016 Summer 一次函数 (线段树)
题目描述 fqk 退役后开始补习文化课啦,于是他打开了数学必修一开始复习函数,他回想起了一次函数都是 f(x)=kx+b的形式,现在他给了你n个一次函数 fi(x)=kix+b,然后将给你m个操作,操 ...
- EDA课设-交通灯-Verilog版----FPGA--004
分得到析四个状态: S1: 主干道(绿灯亮) ,支干道(亮红灯):--40S S1: 主干道 (黄灯亮) ,支干道(亮红灯):--4S S1: 主干道 (亮红灯),支干道(绿灯亮):--20S S1: ...
- angualr6 引入iframe
项目开发中需要在angular项目中嵌入iframe窗口,上网搜索了相关文档,不是很多,但是总算是把功能实现了,现记录一下,便于后期查看: step1:在.html中放入需要承载内容的div,并定义好 ...
- error C2872: ‘ofstream’ : ambiguous symbol
转自VC错误:http://www.vcerror.com/?p=1123 问题描述: 编译时出现: error C2872: 'ofstream' : ambiguous symbol error ...
- MVC和WebApi 使用get和post 传递参数。 转载https://blog.csdn.net/qq373591361/article/details/51508806
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq373591361/article/details/51508806我们总结一下用js请求服务器的 ...
- ccf 201809-3 元素选择器
一.思路: 1.将结构化文档的每一行处理成一个节点(可定义一个结构体,成员包含标签tag.属性id.层级level.祖先所在行数father). 2.然后整个结构化文档就成了一个树形结构,可从任一节点 ...
- <转>Excel生成guid、uuid
Excel生成guid.uuid 原文地址:https://www.cnblogs.com/jory/p/7718305.html 1.Excel生成guid,uuid 格式:600d65bc- ...