Spring Boot + Redis 实现Shiro集群
为实现Web应用的分布式集群部署,要解决登录session的统一。本文利用shiro做权限控制,redis做session存储,结合spring boot快速配置实现session共享。
1、引入相关依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.3.2</version>
- </dependency>
2、Redis相关
2.1.redis配置
- spring.redis.host=localhost
- spring.redis.port=6379
- spring.redis.password=
2.2.redis缓存的对象必须序列化,通用序列化
- import org.springframework.core.convert.converter.Converter;
- import org.springframework.core.serializer.support.DeserializingConverter;
- import org.springframework.core.serializer.support.SerializingConverter;
- import org.springframework.data.redis.serializer.RedisSerializer;
- import org.springframework.data.redis.serializer.SerializationException;
- /**
- * redis序列化对象
- */
- public class RedisObjectSerializer implements RedisSerializer<Object> {
- private Converter<Object, byte[]> serializer = new SerializingConverter();
- private Converter<byte[], Object> deserializer = new DeserializingConverter();
- static final byte[] EMPTY_ARRAY = new byte[0];
- public Object deserialize(byte[] bytes) {
- if (isEmpty(bytes)) {
- return null;
- }
- try {
- return deserializer.convert(bytes);
- } catch (Exception ex) {
- throw new SerializationException("Cannot deserialize", ex);
- }
- }
- public byte[] serialize(Object object) {
- if (object == null) {
- return EMPTY_ARRAY;
- }
- try {
- return serializer.convert(object);
- } catch (Exception ex) {
- return EMPTY_ARRAY;
- }
- }
- private boolean isEmpty(byte[] data) {
- return (data == null || data.length == 0);
- }
- }
2.3 RedisTemplate 配置
- import org.springframework.cache.CacheManager;
- 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.serializer.StringRedisSerializer;
- /**
- * redis 配置
- */
- @Configuration
- public class RedisConfig {
- @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<Object, Object>();
- template.setConnectionFactory(factory);
- template.setKeySerializer(new StringRedisSerializer());
- template.setValueSerializer(new RedisObjectSerializer());
- return template;
- }
- }
3.Redis实现shiro的SessionDao存取session
- import java.io.Serializable;
- import java.util.concurrent.TimeUnit;
- import javax.annotation.Resource;
- 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 org.springframework.stereotype.Component;
- /**
- * redis实现共享session
- */
- @Component
- public class RedisSessionDAO extends EnterpriseCacheSessionDAO {
- private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
- // session 在redis过期时间是30分钟30*60
- private static int expireTime = 1800;
- private static String prefix = "weiyou-shiro-session:";
- @Resource
- private RedisTemplate<String, Object> redisTemplate;
- // 创建session,保存到数据库
- @Override
- protected Serializable doCreate(Session session) {
- Serializable sessionId = super.doCreate(session);
- logger.debug("创建session:{}", session.getId());
- redisTemplate.opsForValue().set(prefix + sessionId.toString(), session);
- return sessionId;
- }
- // 获取session
- @Override
- protected Session doReadSession(Serializable sessionId) {
- logger.debug("获取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);
- logger.debug("获取session:{}", session.getId());
- String key = prefix + session.getId().toString();
- if (!redisTemplate.hasKey(key)) {
- redisTemplate.opsForValue().set(key, session);
- }
- redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
- }
- // 删除session
- @Override
- protected void doDelete(Session session) {
- logger.debug("删除session:{}", session.getId());
- super.doDelete(session);
- redisTemplate.delete(prefix + session.getId().toString());
- }
- }
4.实现cache共享
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.List;
- import java.util.Set;
- import java.util.concurrent.TimeUnit;
- import org.apache.shiro.cache.Cache;
- import org.apache.shiro.cache.CacheException;
- import org.springframework.data.redis.core.RedisTemplate;
- @SuppressWarnings("unchecked")
- public class ShiroCache<K, V> implements Cache<K, V> {
- private static final String REDIS_SHIRO_CACHE = "weiyou-shiro-cache:";
- private String cacheKey;
- private RedisTemplate<K, V> redisTemplate;
- private long globExpire = 30;
- @SuppressWarnings("rawtypes")
- public ShiroCache(String name, RedisTemplate client) {
- this.cacheKey = REDIS_SHIRO_CACHE + name + ":";
- this.redisTemplate = client;
- }
- @Override
- public V get(K key) throws CacheException {
- redisTemplate.boundValueOps(getCacheKey(key)).expire(globExpire, 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);
- }
- }
实现shiro 的CacheManager
- import javax.annotation.Resource;
- import org.apache.shiro.cache.Cache;
- import org.apache.shiro.cache.CacheException;
- import org.apache.shiro.cache.CacheManager;
- import org.springframework.data.redis.core.RedisTemplate;
- public class RedisCacheManager implements CacheManager {
- @Resource
- private RedisTemplate<String, Object> redisTemplate;
- @Override
- public <K, V> Cache<K, V> getCache(String name) throws CacheException {
- return new ShiroCache<K, V>(name, redisTemplate);
- }
- public RedisTemplate<String, Object> getRedisTemplate() {
- return redisTemplate;
- }
- public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
- this.redisTemplate = redisTemplate;
- }
- }
5.配置
- import java.util.HashMap;
- import java.util.Map;
- import javax.annotation.Resource;
- 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;
- /**
- *
- * @author April.Chen
- */
- //@Configuration
- public class ShiroConfig {
- @Resource
- private RedisSessionDAO sessionDAO;
- @Bean
- public UserRealm getUserRealm() {
- return new UserRealm();
- }
- @Bean
- public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
- return new LifecycleBeanPostProcessor();
- }
- @Bean
- public RedisCacheManager redisCacheManager() {
- return new RedisCacheManager();
- }
- @Bean
- public SessionManager sessionManager() {
- DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
- sessionManager.setSessionDAO(sessionDAO);
- sessionManager.setGlobalSessionTimeout(1800);
- sessionManager.setCacheManager(redisCacheManager());
- return sessionManager;
- }
- @Bean
- public DefaultWebSecurityManager securityManager() {
- DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
- securityManager.setSessionManager(sessionManager());
- securityManager.setCacheManager(redisCacheManager());
- return securityManager;
- }
- @Bean
- public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
- AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
- aasa.setSecurityManager(securityManager());
- return new AuthorizationAttributeSourceAdvisor();
- }
- @Bean
- public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
- DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
- daap.setProxyTargetClass(true);
- return daap;
- }
- @Bean
- public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
- Map<String, String> filterChainDefinitionMap = new HashMap<>();
- ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
- shiroFilterFactoryBean.setSecurityManager(securityManager());
- shiroFilterFactoryBean.setLoginUrl("/login");
- shiroFilterFactoryBean.setSuccessUrl("/index");
- filterChainDefinitionMap.put("/sa/**", "authc");
- filterChainDefinitionMap.put("/**", "anon");
- shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
- return shiroFilterFactoryBean;
- }
- }
Spring Boot + Redis 实现Shiro集群的更多相关文章
- Spring Boot集成Hazelcast实现集群与分布式内存缓存
Hazelcast是Hazelcast公司开源的一款分布式内存数据库产品,提供弹性可扩展.高性能的分布式内存计算.并通过提供诸如Map,Queue,ExecutorService,Lock和JCach ...
- Spring Boot项目配置RabbitMQ集群
//具体参看了配置的源码 org.springframework.boot.autoconfigure.amqp.RabbitProperties //RabbitMQ单机 spring: rab ...
- spring + spring-data-redist + Redis 单机、集群(cluster模式,哨兵模式)
一.单机redis配置 1. 配置redis连接池 <bean id="jedisPoolConfig" class="redis.clients.jedis.Je ...
- Apache shiro集群实现 (七)分布式集群系统下---cache共享
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- Redis 高可用集群
Redis 高可用集群 Redis 的集群主从模型是一种高可用的集群架构.本章主要内容有:高可用集群的搭建,Jedis连接集群,新增集群节点,删除集群节点,其他配置补充说明. 高可用集群搭建 集群(c ...
- Apache shiro集群实现 (八) web集群时session同步的3种方法
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- Apache shiro集群实现 (五)分布式集群系统下的高可用session解决方案
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- Apache shiro集群实现 (四)shiro授权(Authentication)--访问控制
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
随机推荐
- jQuery回车触发事件
举例: 需求:要求回车触发下一步 Html部分 <div style="margin-top: 25px;"> <a href="#" cla ...
- ThinkPHP3.2.3完整版创建前后台入口文件 http://jingyan.baidu.com/article/7e4409533fc1092fc1e2ef53.html
ThinkPHP3.2.3完整版创建前后台入口文件 1 2 3 4 5 6 7 分步阅读 ThinkPHP是为了简化企业级应用开发和敏捷WEB应用开发而诞生的优秀的国产php框架,值得我们去探索学 ...
- String s String s=null和String s="a"区别
原文链接:https://www.cnblogs.com/ipetergo/p/6826909.htmlString s;和String s=null;和String s="a"; ...
- Zookeeper基础使用
部署和运行 脚本 ZooKeeper的bin目录下的脚本见下表 脚本 说明 zkCleanup 清理ZooKeeper历史数据,包括事务日志文件和快照数据文件 zkCli ZooKeeper的一个简易 ...
- atcoder 2643 切比雪夫最小生成树
There are N towns on a plane. The i-th town is located at the coordinates (xi,yi). There may be more ...
- [国家集训队]happiness 最小割 BZOJ 2127
题目描述 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文 ...
- 【Cracking the Code Interview(5th edition)】一、数组与字符串(C++)
1.1 实现一个算法,确定一个字符串的所有字符是否全都不同.不允许使用额外的数据结构. 解答:这里假定字符集为ASCII码,可以与面试官沟通确认字符串使用的字符集.由于字符集是有限的,建立一个数组模拟 ...
- 钉钉jsapi免登获取code中,出现对应企业没有某域名微应用
在使用jsapi中.出现 {"errorMessage":"对应企业没有某域名微应用",:"errorCode":"3" ...
- Unity 动画系统目录 之 Animation
返回 Unity 动画系统目录 官方文档 Animation:https://docs.unity3d.com/ScriptReference/Animation.html Animator:http ...
- CF D. Fair(思维+DFS)
http://codeforces.com/contest/987/problem/D 题目大概: 给出一个n个城镇m条边的图,给出每个城镇拥有的特产(可能多个城镇有相同特产).有k种不同特产. 要求 ...