Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring Session 的原理

Spring Session 的原理

原创
作者头像
tuhooo
修改于 2022-07-20 00:56:46
修改于 2022-07-20 00:56:46
3340
举报
文章被收录于专栏:乐乐乐乐

原文地址: Spring Session 的原理

欢迎访问我的博客: https://blog.duhbb.com


引言

今天在写一个对外接口, 这个接口大致原理是在过滤器中通过 token 获取用户信息然后创建 session, 后续的流程就是 Controller -> Service -> Dao 了.

这次开发没有像之前那样愣头愣脑的, 我想了一下, 对方调用的时候是没有 session id 的, 也就是每次认证之后都会创建一个 session. 那这就可能存在一个大问题了, 假设调用次数非常多的话, 会创建茫茫多的 session, 可能会击垮系统.

所以我的看下我们系统中是如何使用 session 的.

Spring Session 探索

代码跟踪

第一件做的是就是断点 request 获取 session 的代码, 果然是有说法啊!

代码语言:java
AI代码解释
复制
request.getSession()

file

代码语言:java
AI代码解释
复制
@SuppressWarnings("deprecation")

@Order(SessionRepositoryFilter.DEFAULT\_ORDER)

public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter {

    public static final String SESSION\_REPOSITORY\_ATTR = SessionRepository.class.getName();



    public static final int DEFAULT\_ORDER = Integer.MIN\_VALUE + 50;



    private final SessionRepository<S> sessionRepository;



    private ServletContext servletContext;



    private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();

file

代码语言:java
AI代码解释
复制
public void setAttribute(String name, Object value) {

    // 还挺讲究的, 设置 attr 之前先 checkState

    checkState();

    session.setAttribute(name, value);

}

checkState() 也很简单, 就是校验 invalidated 是否为 true.

代码语言:java
AI代码解释
复制
private void checkState() {

    if(invalidated) {

        throw new IllegalStateException("The HttpSession has already be invalidated.");

    }

}

光从代码找还是有点难找的, 不过还是找到了:

file

file

file

验证 redis 中的数据

上个 debug 的 session 的 key 是: spring:session:sessions:62359810-d2cb-4378-a619-e2c31bb8242c, 看上去是存了一个 hash 结构.

redis 中获取 hash 的命令是:

代码语言:shell
AI代码解释
复制
HGETALL hkey

执行一下:

代码语言:shell
AI代码解释
复制
127.0.0.1:6379> HGETALL spring:session:sessions:62359810-d2cb-4378-a619-e2c31bb8242c

1) "maxInactiveInterval"

2) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x01Q\x80"

3) "lastAccessedTime"

4) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82\x00:\x99\x0e"

5) "creationTime"

6) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82\x00:\x99\x0e"

里面好像还是没有业务数据, 艹, 发现我傻逼了, 断点还没放开,redis 压根还没存这个业务数据.

再执行一遍:

代码语言:shell
AI代码解释
复制
127.0.0.1:6379> HGETALL spring:session:sessions:62359810-d2cb-4378-a619-e2c31bb8242c

 1) "creationTime"

 2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82\x00:\x99\x0e"

 3) "lastAccessedTime"

 4) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82\x00D\x1f["

 5) "sessionAttr:HumanSession"

 6) "\xac\xed\x00\x05sr\x00#cn.com.xxx.base.bean.HumanSession\x8f\xbb\xb6d\xf6\x04\x8a\xd5\x02\x00\x1fD\x00\x0bcoordinateXD\x00\x0bcoordinateYZ\x00\tvalidFlagL\x00\x0fautoReceiveFlagt\x00\x13Ljava/lang/Integer;L\x00\x0ebrowserVersiont\x00\x12Ljava/lang/String;L\x00\ndataUnitIDq\x00~\x00\x01L\x00\afromCasq\x00~\x00\x01L\x00\tfromTokenq\x00~\x00\x01L\x00\thumanCodeq\x00~\x00\x02L\x00\ahumanIDq\x00~\x00\x01L\x00\thumanNameq\x00~\x00\x02L\x00\nhumanStyleq\x00~\x00\x02L\x00\ninvalidMsgq\x00~\x00\x02L\x00\x02ipq\x00~\x00\x02L\x00\rleaderLevelIDq\x00~\x00\x01L\x00\x05logIDq\x00~\x00\x01L\x00\tosVersionq\x00~\x00\x02L\x00\npatrolFlagq\x00~\x00\x01L\x00\bportraitq\x00~\x00\x02L\x00\bproxyUrlq\x00~\x00\x02L\x00\nregionCodeq\x00~\x00\x02L\x00\bregionIDq\x00~\x00\x01L\x00\nregionNameq\x00~\x00\x02L\x00\nregionTypeq\x00~\x00\x01L\x00\bserverIpq\x00~\x00\x02L\x00\x06targetq\x00~\x00\x02L\x00\ttelMobileq\x00~\x00\x02L\x00\aunionIDq\x00~\x00\x01L\x00\x06unitIDq\x00~\x00\x01L\x00\bunitNameq\x00~\x00\x02L\x00\buserNameq\x00~\x00\x02xp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01pppsr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00q\x00~\x00\x06t\x00\rwizdom:100433sq\x00~\x00\x04\x00\x01\x88Qt\x00\x05xxxxx\x00\bdarkbluept\x00\x0f192.168.213.161pppq\x00~\x00\x06t\x00\x00ppq\x00~\x00\x06psq\x00~\x00\x04\x00\x00\x00\x01t\x00\t127.0.0.1pt\x00\x0b17700000000sq\x00~\x00\x04\x00\x00\x02\xbesq\x00~\x00\x04\x00\x00\x00\x02t\x00\x0f\xe5\xb8\x82\xe7\x9b\x91\xe7\x9d\xa3\xe4\xb8\xad\xe5\xbf\x83t\x00\x05xxx"

 7) "maxInactiveInterval"

 8) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x01Q\x80"

 9) "sessionAttr:UnionAuthToken"

10) "\xac\xed\x00\x05t\x00$125ba47c-fe01-42b6-b5a2-8a87eb266ddc"

这回有了, 而且还很多.

过期时间如何设置呢?

其实在可以加一个过滤器, 像平常那样设置 session 的过期时间就行:

代码语言:java
AI代码解释
复制
req.getSession().setMaxInactiveInterval(expireTime);

filterChain.doFilter(servletRequest, servletResponse);

session 存储的小结

Spring Session 对 JavaWeb 中的 session 进行了一层包装, 写业务时候的接口都保持不变, 但是底层的存储从 Tomcat 中的内存变成了 Redis, 而且用户还没有感知.

如果可以能用哨兵模式保证 Redis 的高可以, 感觉是不是就解决了分布式 Session 的问题.

Session 的 invalidate 实现

代码语言:java
AI代码解释
复制
HttpSession session = request.getSession();

if(session != null){

    session.removeAttribute(/\* here is your attr name\*/);

    session.invalidate();

}

看来这里主要就是 session.invalidate() 了.

代码语言:java
AI代码解释
复制
// org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper.

// HttpSessionWrapper#invalidate

public void invalidate() {

    checkState();

    this.invalidated = true;

    requestedSessionInvalidated = true;

    setCurrentSession(null);

    sessionRepository.delete(getId());

}

根据 id 删除 session:

代码语言:java
AI代码解释
复制
// org.springframework.session.data.redis.RedisOperationsSessionRepository#delete

public void delete(String sessionId) {

    ExpiringSession session = getSession(sessionId, true);

    if(session == null) {

        return;

    }



    String key = getKey(sessionId);

    expirationPolicy.onDelete(session);



    // always delete they key since session may be null if just expired

    this.sessionRedisOperations.delete(key);

}

onDelete 中也删除数据的:

代码语言:java
AI代码解释
复制
    public void onDelete(ExpiringSession session) {

        long toExpire = roundUpToNextMinute(expiresInMillis(session));

        String expireKey = getExpirationKey(toExpire);

        expirationRedisOperations.boundSetOps(expireKey).remove(session.getId());

    }

盗来的一张图

file

回到我的问题

第三方通过 token 调用接口创建会话的问题很简单, 调用完了之后就可以将 session invalidate 了.

哈哈


原文地址: Spring Session 的原理

欢迎访问我的博客: https://blog.duhbb.com

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一篇教会你写90%的shell脚本!
shell是外壳的意思,就是操作系统的外壳。我们可以通过shell命令来操作和控制操作系统,比如Linux中的Shell命令就包括ls、cd、pwd等等。总结来说,Shell是一个命令解释器,它通过接受用户输入的Shell命令来启动、暂停、停止程序的运行或对计算机进行控制。
用户7118337
2020/04/12
1.9K0
shell-编写shell脚本所需的基础语法
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
洋仔聊编程
2019/09/18
9190
shell-编写shell脚本所需的基础语法
Shell 学习[通俗易懂]
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
全栈程序员站长
2022/09/09
6920
Linux的Shell编程语法集锦
【GiantPandaCV导语】相信在linux服务器环境下完成算法开发和部署的同学,都有使用shell来实现部分自动化功能的经历,本文就来给大家分享我总结的一些shell语法知识,希望对大家有帮助。
BBuf
2021/08/19
1.7K0
Shell base用法描述
bash中的字符串可以用单引号和双引号,其区别就是,单引号内不能解释变量,而双引号内可以解释变量
郭顺发
2023/07/06
3630
Shell
vim调试,/{匹配字符}可以查找相应的位置,N往后查找下一个,shift+N往前。
matt
2022/10/25
1.3K0
Shell 编程语法基础
在Shell脚本中,定义变量直接赋值即可,使用变量时需要在变量名前加美元符号$,注意定义变量时变量名和等号之间不能有空格。 变量名的命名必须遵循以下规则:
嵌入式视觉
2022/09/05
2.3K0
Shell 编程语法基础
shell基础编程(一)
引言:之前的初识shell的内容简单的介绍了一下shell,帮助大家认识了一下shell 的组成,这篇文章就具体的讲解shell有关的知识。如果大家有编程基础的话。接下来几篇的文章读起来都会非常容易。没有的话也没有关系,我尽最大的可能讲的通俗易懂。那么现在就开始吧
找Bug
2022/12/14
4640
shell基础编程(一)
高级shell脚本编程指南_python的快速入门
什么是shell呢?shell是用C语言编写的程序,它是用户使用 Linux 的桥梁。Shell既是一种命令语言,又是一种程序设计语言。简单来说Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。也可以这样认为,linux中的shell就是linux内核的一个外层保护工具,并负责完成用户与内核之间的交互
全栈程序员站长
2022/11/09
3.2K0
高级shell脚本编程指南_python的快速入门
Mac下Shell脚本基础用法
因为不常用shell,老是边用边查,现在做个小笔记。所有内容来源:Shell 教程
傅_hc
2020/11/03
4.9K0
Mac下Shell脚本基础用法
一篇文章让你彻底掌握 Shell
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
硬件开源小站
2023/04/07
2.2K0
4. shell 语法
shell脚本可以直接在命令行中执行,也可以将一套逻辑组织成一个文件,方便复用。 Terminal中的命令行可以看成是一个“shell脚本在逐行执行”。
浪漫主义狗
2022/09/28
2.5K0
Shell基础入门
Shell基础入门 linux系统是如何操作计算机硬件CPU,内存,磁盘,显示器等?使用linux的内核操作计算机的硬件Shell介绍... Shell计算命令 Shell计算命令:expr命令
乐心湖
2021/01/18
2.4K0
Shell基础入门
Linux命令(二)——shell编程
Unix/Linux上常见的Shell脚本解释器有bash、sh、csh、ksh等,习惯上把它们称作一种Shell。我们常说有多少种Shell,其实说的是Shell脚本解释器。
不愿意做鱼的小鲸鱼
2022/09/26
3.9K0
shell 基本语法
jenkins 上构建项目时,经常需要借助 shell 脚本,最近也经常跟服务器打交道,顺便记录些常用命令,方便查阅
请叫我大苏
2019/11/27
1.3K1
Shell编程快速入门指南
字符串可以使用单引号和双引号,单引号中不能包含单引号,即使转义单引号也不次那个,双引号则可以,双引号也可以使用字符串。
用户1515472
2019/07/24
7500
一文掌握shell脚本的基本语法
欢迎大家star我的GitHub:https://github.com/SolerHo/geeks-shell,建议直接使用GitHub来查看排版,发现markdown有错位的情况。
阳光罗诺
2022/01/03
4.2K0
一文掌握shell脚本的基本语法
Shell 快速指南
本文介绍了Linux Shell 的基础知识,包括基本概念、常用命令、快捷键和实用技巧。同时,还介绍了如何通过脚本和工具实现自动化和效率提升。
静默虚空
2018/01/05
3.4K0
Linux Shell编程入门
注意:bash是 Bourne Again Shell 的缩写,是linux标准的默认shell ,它基于Bourne shell,吸收了C shell和Korn shell的一些特性。bash完全兼容sh,也就是说,用sh写的脚本可以不加修改的在bash中执行。
程裕强
2022/05/06
3.1K0
linux shell指令大全整理
所有程序, 包括shell启动的程序, 都能访问环境变量, 在c中通过system()函数执行的结果可以通过环境变量传递回来
全栈程序员站长
2022/11/02
1.7K0
相关推荐
一篇教会你写90%的shell脚本!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档