分布式Shiro,SpringBoot项目Shiro整合Redis

分布式Shiro,SpringBoot项目Shiro整合Redis

====================重要 Begin====================

你的SpringBoot项目已经使用了Shiro,并且可以正常使用。本篇文章的主要目的是将Shiro保存在服务器内存中的session信息改为使用Redis保存session信息
====================重要 End====================

正文开始

0、前情概要

由于shiro不支持分布式场景下使用(可能是支持,但没找到),但是现在项目都是分布式的项目,一个服务要部署多个实例,明显shiro已经无法满足现有的情况。为了使shiro可以在分布式项目中使用,博主查阅了很多资料,其中一个包括引入shiro-redis依赖来实现(并未实操),但是看这个依赖最新的版本还是在2020年,果断放弃(应该有很多漏洞,我司对漏洞的把控极为严格!!!),所以就想着是否可以根据目前掌握的技术和网上的实践来手动实现一下shiro整合redis。以下就是博主的实现过程(码多话少,有事滴滴~)

1、引入依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

2、创建Redis配置类

java 复制代码
/**
 * Redis配置类
 */
@EnableCaching
@Configuration
public class RedisConfiguration {

    @Autowired
    RedisCacheProperties redisCacheProperties;

    @Bean()
    public RedisTemplate<String, Object> redisShiroTemplate(@Autowired RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        // shiro序列化存储session存在问题(https://www.cnblogs.com/ReturnOfTheKing/p/18224205)
        /*template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());*/
        return template;
    }

    @Bean(name = {"cacheKeyGenerator"})
    public KeyGenerator cacheKeyGenerator() {
        return (Object o, Method method, Object... objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName());
            sb.append(method.getName());
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

    @Bean(name = "cacheManager")
    public RedisCacheManager cacheManager(@Autowired RedisConnectionFactory redisConnectionFactory) {

        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(7))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();
        RedisCacheManager.RedisCacheManagerBuilder builder =
                RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory);

        Set<String> cacheNames = new HashSet<>();
        ConcurrentHashMap<String, RedisCacheConfiguration> cacheConfig = new ConcurrentHashMap<>();

        for (Map.Entry<String, Duration> entry : redisCacheProperties.getCacheDuration().entrySet()) {
            cacheNames.add(entry.getKey());
            cacheConfig.put(entry.getKey(), defaultConfig.entryTtl(entry.getValue()));
        }

        RedisCacheManager cacheManager = builder
                .transactionAware()
                .cacheDefaults(defaultConfig)
                .initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(cacheConfig)
                .build();

        return cacheManager;
    }
}
java 复制代码
/**
 * RedisCache参数
 */
@Component
@Getter
public class RedisCacheProperties {

    private final Map<String, Duration> cacheDuration = new HashMap<>();
}

3、继承AbstractSessionDAO,创建自定义RedisSessionDao 类

java 复制代码
/**
 * 自定义RedisSessionDAO
 */
@Component
public class RedisSessionDao extends AbstractSessionDAO {

    @Value("${session.redis.expireTime}")
    private long expireTime;

    @Autowired
    private RedisTemplate<String, Object> redisShiroTemplate;

    private String getKey(String originalKey) {
        return "shiro_redis_session_key_:" + originalKey;
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        redisShiroTemplate.opsForValue().set(getKey(session.getId().toString()), session, expireTime, TimeUnit.SECONDS);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        return sessionId == null ? null : (Session) redisShiroTemplate.opsForValue().get(getKey(sessionId.toString()));
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        if (session != null && session.getId() != null) {
            session.setTimeout(expireTime * 1000);
            redisShiroTemplate.opsForValue().set(getKey(session.getId().toString()), session, expireTime, TimeUnit.SECONDS);
        }
    }

    @Override
    public void delete(Session session) {
        if (session != null && session.getId() != null) {
            redisShiroTemplate.opsForValue().getOperations().delete(getKey(session.getId().toString()));
        }
    }

    @Override
    public Collection<Session> getActiveSessions() {
        return Collections.emptySet();
    }
}

4、在ShiroConfiguration配置类中使用自定义SessionDAO

java 复制代码
@Bean
public SessionManager shiroSessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    //session过期时间
    sessionManager.setGlobalSessionTimeout(expireTime * 1000);
    sessionManager.setSessionDAO(redisSessionDao);
    return sessionManager;
}

问题

往redis中放的数据记得实现 序列化接口,不然会报错!

参考

  1. 分布式shiro,session共享
  2. Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享
  3. shiro org.apache.shiro.session.mgt.SimpleSession对象 反序列化失败
相关推荐
ruleslol1 小时前
Redis 雪崩与服务降级
数据库·redis
paopaokaka_luck2 小时前
基于springboot3+vue3的文山民族文化资源展示与推广平台(协同过滤算法、Echarts 图形化分析)
java·前端·spring boot·学习·echarts
刀锋00013 小时前
Java 工程师转 AI Agent 最短路径:用 Spring 的思维理解 Spring AI 2.0(附 LangChain4j 对照)
java·人工智能·spring boot·spring·ai agent
Java牛马3 小时前
Caffeine 缓存及其应用相关总结
java·redis·缓存·caffeine·数据一致性·本地缓存
尚可签3 小时前
基于 Spring Boot + Vue 的 AI 智能在线订餐系统
vue.js·人工智能·spring boot
软件开发JR4 小时前
基于Web的足球青训俱乐部管理后台系统的设计与开发
java·前端·spring boot·毕业设计
凤山老林4 小时前
基于 Spring Batch 的海量数据迁移与批处理架构:分片、容错与断点续跑
java·spring boot·spring·架构·spring batch
李可以量化4 小时前
Redis 从了解到精通(三)上:量化交易场景下的数据备份与安全配置
redis·python·安全·qmt·ptrade
gis开发之家5 小时前
Spring Boot 4 深度解析——参数接收大全:@RequestParam、@PathVariable、@RequestBody
java·spring boot·后端·spring
大菠萝爱上小西瓜6 小时前
【大数据实战】Spark 2.2.2 完全分布式集群搭建
大数据·分布式·spark