Shiro使用Redis作存储之后更新Session失败的问题

问题

因为想在多个应用之间共享用户的登录态,因此实现了自己的SessionDAO,使用Kryo把SimpleSession序列化然后放到redis之中去,同时也使用了shiro.userNativeSessionManager: true来使用shiro自己的存储。然而之后一直出现丢失更新的问题,例如

Session session = SecurityUtils.getSubject().getSession();
User user = (User) session.getAttribute(MembershipConst.SessionKey.USER);
user.setName("newName");  // 名称没有更新

分析

DEBUG之后发现,从Subject中取到的Session并不是我们在SessionDAO中创建的SimpleSession,而是DelegatingSubject$StoppingAwareProxiedSession,这是一个代理类,本身并不做任何事情,而是通过DelegatingSession调用真正的方法。而DelegatingSession实则也并没有真正的调用SimpleSession,而是调用的SessionManager中的方法:

/**
* @see Session#setAttribute(Object key, Object value)
*/
public void setAttribute(Object attributeKey, Object value) throws InvalidSessionException {
    if (value == null) {
        removeAttribute(attributeKey);
    } else {
        sessionManager.setAttribute(this.key, attributeKey, value);
    }
}

而默认的DefaultSessionManager在进行任何写操作之前总是会先通过SessionDAO读一次,如setAttribute方法

public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
    if (value == null) {
        removeAttribute(sessionKey, attributeKey);
    } else {
        Session s = lookupRequiredSession(sessionKey);
        s.setAttribute(attributeKey, value);
        onChange(s);
    }
}

这就是了,实际上我们并未显式的将Session写回redis,而是更新lastAccessTime的时候一并写回去的,而更新访问时间的时候调用了touch()方法,SessionManager又通过SessionDAO读取了一次,重新读取了redis然后反序列化出一个新的Session,原来Session的各种改动自然也就丢失了。

解决

首先是在SessionDAO上加上缓存,一来避免频繁的redis读取,二来避免出现每次读取返回一个新Session的问题。然后在我们的场景中并不需要最后访问时间,因此重写了ShiroFilterFactoryBean,不在更新最后访问时间,当Session需要更新的时候,直接调用SessionDAO写回redis,避免SessionManager做二传手。

当然这不是完美的解决方案,并发场景下依然会有更新问题。调式中可以看出Shiro通过SessionDAO进行的读写操作非常频繁,显然在设计时并未将它当作一个涉及外部IO的类。因此将Session放在redis实则不是一个好注意,应该考虑其它的机制。

原文地址:https://www.cnblogs.com/narcissu5/p/9937628.html

时间: 2024-09-30 12:50:40

Shiro使用Redis作存储之后更新Session失败的问题的相关文章

SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录

作者:Sans_ juejin.im/post/5d087d605188256de9779e64 一.说明 Shiro是一个安全框架,项目中主要用它做认证,授权,加密,以及用户的会话管理,虽然Shiro没有SpringSecurity功能更丰富,但是它轻量,简单,在项目中通常业务需求Shiro也都能胜任. 二.项目环境 MyBatis-Plus版本: 3.1.0 SpringBoot版本:2.1.5 JDK版本:1.8 Shiro版本:1.4 Shiro-redis插件版本:3.1.0 数据表(

shiro之redis频繁访问问题

目前安全框架shiro使用较为广泛,其功能也比较强大.为了分布式session共享,通常的做法是将session存储在redis中,实现多个节点获取同一个session.此实现可以实现session共享,但session的特点是内存存储,就是为了高速频繁访问,每个请求都必须验证session是否存在是否过期,也从session中获取数据.这样导致一个页面刷新过程中的数十个请求会同时访问redis,在几毫秒内同时操作session的获取,修改,更新,保存,删除等操作,从而造成redis的并发量飙升

Ubuntu上使用Redis数据库存储SessionID并实现Session共享

p { margin-bottom: 0.1in; direction: ltr; color: #00000a; line-height: 120%; text-align: left; orphans: 2; widows: 2 } p.western { font-family: "Liberation Serif", serif; font-size: 12pt } p.cjk { font-family: "Noto Sans CJK SC Regular"

flask中的CBV , flask-session在redis中存储session , WTForms数据验证 , 偏函数 , 对象里的一些小知识

flask中的CBV , flask-session在redis中存储session , WTForms数据验证 , 偏函数 , 对象里的一些小知识 flask中的CBV写法 后端代码 # 导入views from flask import Flask, render_template, views, request app = Flask(__name__) # CBV写法 class Login(views.MethodView):       # 定义一个类,不用装饰器,继承了Method

SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统

1.前言本文主要介绍使用SpringBoot与shiro实现基于数据库的细粒度动态权限管理系统实例. 使用技术:SpringBoot.mybatis.shiro.thymeleaf.pagehelper.Mapper插件.druid.dataTables.ztree.jQuery 开发工具:intellij idea 数据库:mysql.redis 2.表结构还是是用标准的5张表来展现权限.如下图:image 分别为用户表,角色表,资源表,用户角色表,角色资源表.在这个demo中使用了mybat

SpringMVC集成shiro和redis

记录用maven集成shiro和redis. 先是代码结构: 然后是web.xml 1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://xmlns.jcp.org/xml/ns/javaee" 4 xsi:schemaL

Redis内存存储结构分析

五竹,20110418 Redis: A persistent key-value database with built-in net interface written in ANSI-C for Posix systems 1 Redis 内存存储结构 本文是基于 Redis-v2.2.4 版本进行分析. 1.1 Redis 内存存储总体结构 Redis 是支持多key-value数据库(表)的,并用 RedisDb 来表示一个key-value数据库(表). redisServer 中有

session的官方定义是:Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。 说白了session就是一种可以维持服务器端的数据存储技术。session主要有以下的这些特点: 1. session保存的位置是在服务器端 2. session一般来说是要配合cookie使用,如果是浏览器禁用了cookie功

session的官方定义是:Session:在计算机中,尤其是在网络应用中,称为"会话控制".Session 对象存储特定用户会话所需的属性及配置信息. 说白了session就是一种可以维持服务器端的数据存储技术.session主要有以下的这些特点: 1. session保存的位置是在服务器端 2. session一般来说是要配合cookie使用,如果是浏览器禁用了cookie功能,也就只能够使用URL重写来实现session存储的功能 3. 单纯的使用session来维持用户状态的话

用Redis作Mysql数据库缓存

使用redis作mysql数据库缓存时,需要考虑两个问题: 1.确定用何种数据结构存储来自Mysql的数据; 2.在确定数据结构之后,用什么标识作为该数据结构的键. 直观上看,Mysql中的数据都是按表存储的;更微观地看,这些表都是按行存储的.每执行一次select查询,Mysql都会返回一个结果集,这个结果集由若干行组成.所以,一个自然而然的想法就是在Redis中找到一种对应于Mysql行的数据结构.Redis中提供了五种基本数据结构,即字符串(string).列表(list).哈希(hash