使用SpringSession管理分布式会话时遇到的反序列化问题
关于SpringSession相关的介绍和使用指南,可移步如下网址:
【SpringSession管理分布式系统的会话Session】
https://www.cnblogs.com/captainad/p/10861006.html
问题浮现
我们在使用SpringSession时(其实在问题出现时,我们并没有意识到和这儿有关联),遇到了一个隐藏较深的问题。我们像往常一样,在用户登录成功之后,将用户的实体类信息实例化保存到了session中,且session最后保存到了redis里面,这个过程其实是没有什么问题的。
随着时间的推移业务的迭代变化,用户实体需要为更新的需求增加额外的字段,于是我们顺其自然的在类中追加其他String类型的属性,结果上了测试环境,发现我们无法进入到登录页面,从浏览器控制台上我们发现这个请求因为无法获取正确的鉴权而不断报302,并且浏览器错误主页提示我们“清除浏览器cookie”,当我们照做之后,登录页面神奇般的可以打开并且我们能够顺利登录系统和使用系统了。在我们回到服务端查看后台日志时,我们看到时在redis中出现了反序列化的问题,而且日志报的很蹊跷,并不是我们的业务代码的某一行直接抛出的日志,这给我们定位问题带了一点麻烦。
分析问题
首先反序列化出现问题,我们首先能够想到的就是给用户实体加了额外属性导致的,但是为何会出现反序列化问题呢?原因看起来很简单,我们的用户实体类确实实现了Serializable接口,但是我们并没有显示的写出序列化的serialVersionUID,这在实体变更了属性字段之后反序列化回来,就一定会反序列化失败的,因为在你没有指定序列号serialVersionUID号的时候,系统默认都是以类名称类属性等来自动生成的,不同的属性名称得出的序列化值肯定是不一样的,解决的方法很简单,就是在初始化类的时候,主动写名序列化serialVersionUID号。
但是我们的程序现在已经在生产环境了,无法做到这一步,因为按照新的类来生成序列号,显然是前后不一致的,给新加的字段加上transient字段?这种做法也步行,这意味着这个字段的值将无法序列化,后面其他系统肯定是使用不了的,这些额外增加的字段就没有存在意义了。
在对报错日志进行抽丝剥茧逐行分析之后,发现这个异常居然和SpringSession有关系。
因为我们在用户成功登录系统之后,使用了request.getSession().setAttribute("xx", "xx")操作,我们将我们的用户实体放到了session中,并由SpringSession成功的放到了Redis里面,并且我们的会话时长是48小时;在我们新的实体更新进来之后,系统一发布,打开登录页面系统对SpringSession中的内容进行初始化,就发现了用户实体无法从Redis中初始化回来,因为前后的序列化号对不上了,于是就疯狂的报反序列化失败,这是根本问题所在。
解决问题
那怎么解决呢?很明显,聪明的浏览器给了我们第一个解决方案,那就是清除浏览器Session。
因为上面我们说到,SpringSession会在浏览器里面自动设置一个名为SESSION的cookie值,这个值是为了后台服务能够在Redis中找到之前存放的Session以便能够识别是统一用户,当浏览器清除了Cookie之后,这个SESSION的值就失去了,于是系统重新为这个浏览器发起请求的用户重新生成SESSION值,这个请求也就无法在redis中找到对应的Session会话,也就没有将用户实体反序列化这个过程,登录程序当然能够正常进行,而且往后的交互都是正常的了。
那么还有另外的解决方法,从Redis端,既然SpringSession会用这个SESSION去Redis中找对应的值,那么我们就将这个Redis中存放的Session内容进行清除,找不到内容自然就无需反序列化了,不过此时正常访问的用户,也会因为请求没有找到统一的Session而认为是会话过期强制退出。这里在redis中清除内容的方式,可以使用下面的命令:
$ redis-cli keys '*' | xargs redis-cli del
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
第三种方式呢,就是从代码端改造,反序列化失败的本质就是原来的类新增了属性,而且类没有显示的指定序列化serialVersionUID,那么我们变通一下,保持原来的类不变呗,新增的属性放到一个继承了这个用户实体类的类当中,后面我们就用这个新的类,保持老的类不变(属性类型和数量等的都不变),那就行了。是的,就是使用新类的方式,新类继承用户实体类,保持用户实体类不变,新类当中增加需要补充的属性字段,同时需要序列化,这样SpringSession在初始化时,它依然能够反序列找到原来没有变通的那个类,我们自己也能够很好的实现我们新的需求了。
问题总结
通过上面的分析我们可以发现,其实SpringSession是替换了HttpSession并在序列化之后存入了Redis里面的,隐藏较深的持久化对外部没有写明serialVersionUID的对象进行的变化较为敏感,如果一旦遗漏了这个细节,在日后变更属性之后,可能就会暴露出这个问题,而且因为牵扯到用户登录鉴权,这种影响算是较为恶劣,所以平时应该留意这种小错误,尽可能在对象实现序列化之后写名serialVersionUID,相信这是一种好习惯,当然,可以设置IDEA让它自动检测,如果你实现了Serializable接口但是没有生成serialVersionUID就报错提醒。希望这次的问题分析能够对日后使用SpringSession有所帮助,能够想周到一点,想深入一点。
使用SpringSession管理分布式会话时遇到的反序列化问题的更多相关文章
- 使用SpringSession管理分布式系统的会话Session
在我方供应链项目分布式部署的环境下,需要在统一网关服务中管理访问的Session,即无论访问请求路由到哪一个网关服务环境,使用的都是相同的HttpSession,这样就保证了在用户登录之后,能够使用统 ...
- 第二十三章 多项目集中权限管理及分布式会话——《跟我学Shiro》
二十三章 多项目集中权限管理及分布式会话——<跟我学Shiro> 博客分类: 跟我学Shiro 跟我学Shiro 目录贴:跟我学Shiro目录贴 在做一些企业内部项目时或一些互联网后台时 ...
- 补习系列(15)-springboot 分布式会话原理
目录 一.背景 二.SpringBoot 分布式会话 三.样例程序 四.原理进阶 A. 序列化 B. 会话代理 C. 数据老化 小结 一.背景 在 补习系列(3)-springboot 几种scope ...
- 利用 ssh 的用户配置文件 config 管理 ssh 会话
http://dhq.me/use-ssh-config-manage-ssh-session 利用 ssh 连接远程服务器,一般都要输入以下类似命令: 1 ssh user@hostname -p ...
- 004-restful应用构建、分布式会话、测试工具简介
一.概述 什么是rest(表述性状态转移,Representational State Transfer)是一种架构风格.他定义了创建可扩展Web服务的最佳实践. 1.Richardson成熟度模型 ...
- 利用ssh的用户配置文件config管理ssh会话
通常利用 ssh 连接远程服务器,一般都要输入以下类似命令: ssh user@hostname -p port 如果拥有多个 ssh 账号,特别是像我这种喜欢在终端里直接 ssh 登陆,不用 PuT ...
- 阶段5 3.微服务项目【学成在线】_day08 课程图片管理 分布式文件系统_06-分布式文件系统研究-fastDFS安装及配置文件说明
3 fastDFS入门 3.1fastDFS安装与配置 3.1.1 导入虚拟机 对fastDFS的安装过程不要求学生掌握,可以直接导入老师提供虚拟机. 1.使用Vmware打开虚拟机配置文件“Cent ...
- 使用Redis实现分布式会话
1. 概述 传统的单体应用中,用户是否登录,通常是通过从Tomcat容器的session中获取登录用户信息判断的. 但在分布式的应用中,通常负载均衡了多台Tomcat,每台Tomcat都有自己独立的s ...
- Dapr中国社区活动之 分布式运行时开发者日 (2022.09.03)
自2019年10月首次发布以来,Dapr(Distributed Application Runtime,分布式应用运行时)因其"更稳定"."更可靠".&quo ...
随机推荐
- 使用showMessageDialog显示消息框
-----------------siwuxie095 工程名:TestJOptionPane 包名:com.siwuxie095.showdialog ...
- OpenGL — GLFW — 颜色
OpenGL - GLFW - 颜色 参考教程:https://learnopengl-cn.readthedocs.io/zh/latest/02%20Lighting/01%20Colors/ 既 ...
- R语言简单作图
以下函数只为满足常用的若干作图需求. 基本作图: plot(x).plot(x, y) #散点图,最多两个变量 #可使用参数type生成不同的效果图.常用'l'.'o'.'h',分别为折线图, ...
- super关键字主要有以下两种用途
super关键字主要有以下两种用途. 1.调用父类的构造方法 子类可以调用由父类声明的构造方法.但是必须在子类的构造方法中使用super关键字来调用.其具体的语法格式如下: super([参数列表]) ...
- 由count(sno)和count(cno)引发的思考
最近在练习sql语句,在一个select查询语句上有理解性偏差,现整理汇总下相关知识点. 首先,说下这个问题吧. 问题是:查询选课人数大于等于2人的课程编号以及选课的人数 具体的表结构信息: 我自己的 ...
- Spring入门第六课
XML配置里的Bean自动装配 Spring IOC容器可以自动装配Bean.需要做的仅仅是在<bean>的autowire属性里指定自动装配的模式 ByType(根据类型自动装配):若I ...
- Linux操作系统的内存使用方法详细解析
我是一名程序员,那么我在这里以一个程序员的角度来讲解Linux内存的使用. 一提到内存管理,我们头脑中闪出的两个概念,就是虚拟内存,与物理内存.这两个概念主要来自于linux内核的支持. Linux在 ...
- 更改数据,ExecuteNonQuery()
using (mycon) { mycon.Open(); string MyTime; DateTime dtDate; MyTime = textBox1.Text.ToString(); str ...
- 2018ICPC徐州区域赛网络赛B(逆序枚举或者正序深度搜索)
#include<bits/stdc++.h>using namespace std;int n,m,k,l;int x[1007],y[1007],z[1007];int dp[1007 ...
- 【转载】【爬坑记录】hyperledger caliper 性能测试工具使用的一些问题记录
原文: https://blog.csdn.net/raogeeg/article/details/82752613 安装方法详见:https://github.com/hyperledger/cal ...