springboot+shiro+redis实现session共享和cache共享

文章详细介绍了在分布式环境中,如何利用ApacheShiro安全框架结合Redis来实现session和cache的共享。通过自定义RedisSessionDAO和ShiroRedisCacheManager,重写Shiro的session管理和缓存管理,确保用户在不同服务器间切换时保持登录状态。此外,还提供了SpringBoot整合Redis的相关配置和代码示例。

在分布式应用中,若是使用了负载均衡,用户第一次访问,连接的A服务器,进行了登录操作进入了系统,当用户再次操作时,请求被转发到了B服务器,用户并没有在B进行登录,此时用户又来到了登录页面,这是难以理解和接受的,这就引出了session共享。
对于shiro安全框架如何实现session共享?shiro共享分为两方面,一个是session共享,一个是cache共享。下面聊聊在springboot工程中整合shiro框架,并通过redis实现session共享和cache共享。

1、shiro

Apache Shiro 是 Java 的一个安全框架,提供认证、授权、加密、会话管理、与 Web 集成、缓存等。
在这里插入图片描述

  • SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
    SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
  • SessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
  • CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
  • SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

其中各个插件的依赖关系如下:
shiro-spring包:shiro-core子包和shiro-web子包
shiro-redis包:shiro-core子包
AuthorizationAttributeSourceAdvisor-》shiro-spring插件
ShiroFilterFactoryBean-》shiro-spring插件
DefaultWebSecurityManager-》shiro-web插件
DefaultWebSessionManager-》shiro-web插件
SecurityManager-》shiro-core插件
SessionManager-》shiro-core插件
CacheManager-》shiro-core插件
AbstractSessionDAO-》shiro-core插件
SessionDAO-》shiro-core插件
Cache-》shiro-core插件
RedisCache-》shiro-redis插件
RedisCacheManager-》shiro-redis插件
RedisSessionDAO-》shiro-redis插件
RedisClusterManager-》shiro-redis插件
RedisManager-》shiro-redis插件

通过上面可知,通过redis重写Cache、CacheManager、SessionDAO的实现类实现session共享和cache共享。其中shiro-redis插件也实现了session共享和cache共享,可参考自己实现一遍。

2、springboot整合redis

新建springboot工程,版本为2.7.x版本,引用redis依赖。

使用的是RedisTemplate来集中存储session和cache,一个列化工具继承RedisSerializer

public class SerializeUtils implements RedisSerializer {

    private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class);

    public static boolean isEmpty(byte[] data) {
        return (data == null || data.length == 0);
    }

    /**
     * 序列化
     * @param object
     * @return
     * @throws SerializationException
     */
    @Override
    public byte[] serialize(Object object) throws SerializationException {
        byte[] result = null;

        if (object == null) {
            return new byte[0];
        }
        try (
                ByteArrayOutputStream byteStream = new ByteArrayOutputStream(128);
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream)
        ){

            if (!(object instanceof Serializable)) {
                throw new IllegalArgumentException(SerializeUtils.class.getSimpleName() + " requires a Serializable payload " +
                        "but received an object of type [" + object.getClass().getName() + "]");
            }

            objectOutputStream.writeObject(object);
            objectOutputStream.flush();
            result =  byteStream.toByteArray();
        } catch (Exception ex) {
            logger.error("Failed to serialize",ex);
        }
        return result;
    }

    /**
     * 反序列化
     * @param bytes
     * @return
     * @throws SerializationException
     */
    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {

        Object result = null;

        if (isEmpty(bytes)) {
            return null;
        }
        try (
                ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
                ObjectInputStream objectInputStream = new ObjectInputStream(byteStream)
        ){
            result = objectInputStream.readObject();
        } catch (Exception e) {
            logger.error("Failed to deserialize",e);
        }
        return result;
    }
}

RedisConfig配置类

@Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // 1.创建RedisTemplate对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        // 2.加载Redis配置
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        // 3.配置key序列化
        RedisSerializer<?> stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // 4.配置Value序列化
//        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
//        ObjectMapper objMapper = new ObjectMapper();
//        objMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        objMapper.activateDefaultTyping(objMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
//        jackson2JsonRedisSerializer.setObjectMapper(objMapper);
        SerializeUtils serializeUtils = new SerializeUtils();
        redisTemplate.setValueSerializer(serializeUtils);
        redisTemplate.setHashValueSerializer(serializeUtils);
        // 5.初始化RedisTemplate
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

RedisService工具类

public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //=============================common============================
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     */
    public void expire(String key,long time){
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String ke
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值