Springboot——关于Springboot线程池时使用ThreadLocal 类的一个小小的漏洞

问题描述

前端的使用ajax发送了一个请求到后端

后端自定义了一个线程上下文和实现了一个拦截器Interceptor

public class BaseContext {public static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void setCurrentId(int id) {threadLocal.set(id);}public static int getCurrentId() {return threadLocal.get();}public  static Integer get(){return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}}
/*** jwt令牌校验的拦截器*/
@Component
@Slf4j
public class JwtTokenInterceptor implements HandlerInterceptor {@Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt*  * @param request* @param response* @param handler* @return* @throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}//        String token = request.getHeader(jwtProperties.getTokenName());//1、从请求头中获取令牌String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7); // 去除 "Bearer " 前缀// 现在你可以使用提取到的token进行处理}//2、校验令牌try {log.info("jwt校验:{}", token);Claims claims = JwtUtil.parseJWT(jwtProperties.getSecretKey(), token);int id = Integer.parseInt(claims.get("id").toString());log.info("当前用户id:{}", id);if(BaseContext.get()!=null)System.out.println("从线程池取出来的线程的已经存在的用户id:"+BaseContext.getCurrentId());BaseContext.setCurrentId(id); //存入线程上下文,每个用户请求独享一个线程,不会冲突System.out.println("刚刚更新的用户ID"+BaseContext.getCurrentId());//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}
}

然后拦截所有的非登录请求.

/*** 配置类,注册web层相关组件*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {@Autowiredprivate JwtTokenInterceptor jwtTokenInterceptor;/*** 注册自定义拦截器* @param registry*/protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");registry.addInterceptor(jwtTokenInterceptor).addPathPatterns("/hospital/**").excludePathPatterns("/hospital/user/login");}
}

然后可以看见拦截路径都是前面要带一个"/hospital"的,并且每一个非login的请求都必须在请求头里面携带一个token,在拦截到的时候的要取出里面封装好的ID存入自定义的线程上下文。但是后端Controller层不小心写了一些不带/hospital前缀的请求路径,导致那些请求全都绕过了拦截器,没有在自定义的线程上下文里面存进去应存的id,但是我在服务层的所有方法都会在执行前先从线程上下文获取id,再通过id获取用户权限。

所以按道理来说,最开始的那个前端的ajax请求即使在请求头里面携带了token也是没办法获取响应,因为没有被拦截存id,服务层获取不到自定义的线程上下文里面的id。

但是实际上却不是这样,那个前端请求有时候可以获得响应,有时候又不行。

 这就很奇怪了,明明那个请求雀氏没有经过拦截器,因为拦截器里面的日志语句一点反应也没有。

在那之前先了解一下springboot的线程池。

Springboot线程池

Springboot应用程序会为每一个用户请求分配一个独立的用户线程。

每个HTTP请求通常都会由一个独立的线程来处理,线程是请求的隔离单位。当一个请求到达时,Spring Boot会分配一个新的线程来处理该请求,这个线程会执行请求处理方法,并可以访问线程局部变量(如ThreadLocal中的变量)。

不同用户的请求会触发不同的线程,它们之间不会共享线程上下文,因此线程局部变量中的数据不会被其他用户的请求访问到。

但需要注意的是,在一些特殊情况下,例如使用线程池来处理请求时,线程可能会被重用。

重点Spring Boot应用程序通常使用线程池来管理和分配处理请求的线程。

在Spring中,Spring会在请求处理结束后自动清理线程上下文中的数据,但如果你手动创建线程或使用自定义线程池,需要自己负责线程上下文的清理。

所以我在上面自定义的线程上下文是不会被自动清理的。包括里面的数据。

解决问题

了解了Springboot应用程序中的线程池的机制之后可以知道,我在上面出现的那个时有时无的问题就是这么来的,首先一个别的正常的进入了拦截器的请求A被分配了一个用户线程A,然后在自定义线程上下文里面留下了一个用户id,结束请求之后,这个用户线程的自定义线程上下文没有被清理直接回到了线程池里面。

然后一个可以绕过拦截器的请求B进入之后被springboot从线程池里面拿出了刚刚回收的线程A的用户线程分配给了B,然后用户线程B在服务层成功从线程上下文里面取出了请求A存进去的用户A的id。然后就奇妙的现象就这么出现了。

修复代码之后

这一次发的请求绝对会被拦截器拦截。然后修改了拦截器里面的代码,在存入用户id之后先尝试从自定义线程上下文里面获取用户ID。

 按照上面的推论,我这里是有可能获得别的请求在线程留下的用户id的。

结果也果然如此,试了几次之后输出如下

在存入前先获取了一个无关的用户id. 

ThreadLocal类

ThreadLocal 是Java中的一个类,它用于创建线程局部变量。线程局部变量是一种特殊的变量,每个线程都有自己独立的副本,线程之间不共享这些变量的值。这使得线程可以在不干扰其他线程的情况下存储和访问自己的数据。

你提到的 new ThreadLocal<>(); 是创建一个新的 ThreadLocal 实例的方式,通常用于定义和管理线程局部变量。例如: 

ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

在上面的示例中,我们创建了一个 ThreadLocal 实例,该实例可以存储整数类型的线程局部变量。每个线程都可以通过这个 ThreadLocal 实例来访问自己的整数值,而不会影响其他线程的值。

ThreadLocal 主要用于在多线程环境下为每个线程存储和管理自己的数据,它在一些场景下非常有用,例如实现线程安全的数据库连接、用户身份验证、跟踪用户会话等。但要小心使用,确保在适当的时候清理 ThreadLocal 值,以避免潜在的内存泄漏问题。

破案总结!

在ThreadLocal类的时候要注意清理数据。

如果一个线程在线程池中被取出,然后在其执行过程中创建了 ThreadLocal 的实例并存入一些数据,然后将线程放回线程池,那么在下一次从线程池中取出同一个线程时,可能会看到之前存入的数据,因为 ThreadLocal 的值与线程相关,而不是与线程池相关。

这是因为 ThreadLocal 在每个线程内部维护了一个独立的副本(线程局部变量),这些副本在不同线程之间是隔离的。如果一个线程在 ThreadLocal 中存储了数据,那么只有同一个线程可以访问和修改这些数据。当线程被放回线程池时,线程池并不会主动清理线程内的 ThreadLocal 值,这意味着下一次取出同一个线程时,可能仍然可以看到之前存入的数据。

这种行为可以在某些情况下带来便利,但也需要谨慎使用,因为如果不正确管理 ThreadLocal 值,可能会导致内存泄漏或不一致的数据访问。在使用线程池时,特别要注意在线程结束后清理 ThreadLocal 值,以避免潜在的问题。通常,可以使用 ThreadLocal.remove() 方法来手动清理 ThreadLocal 值。在线程结束时或任务执行完成后调用 remove() 可以确保 ThreadLocal 值被清除,从而避免对下一次任务的影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/147636.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

克服网络安全压力:如何掌控无限的云数据

管理云中的数字风险比以往任何时候都更加重要。数字化转型引发的云数据呈指数级增长&#xff0c;为安全分析师创造了一个更大的威胁环境。随着威胁行为者继续危害组织最敏感的数据&#xff0c;这一挑战将会加剧。 预计未来五年全球网络犯罪成本将激增&#xff0c;从 2022 年的…

Java笔记六(面向对象:类与对象)

面向对象编程的本质&#xff1a;以类的方式组织代码&#xff0c;以对象的组织&#xff08;封装&#xff09;数据 抽象 三大特征&#xff1a;封装 继承 多态 从认识角度考虑是先有对象后有类。对象&#xff0c;是具体的事物。类&#xff0c;是抽象的&#xff0c;是对对象的抽…

Ubantu 20.04 卸载与安装 MySQL 5.7 详细教程

文章目录 卸载 MySQL安装 MySQL 5.71.获取安装包2.解压并安装依赖包3.安装 MySQL4.启动 MySQL 扩展开启 gtid 与 binlog 卸载 MySQL 执行以下命令即可一键卸载&#xff0c;包括配置文件目录等。 # 安装sudo软件 apt-get install sudo -y # 卸载所有以"mysql-"开头的…

stable diffusion学习笔记【2023-10-2】

L1&#xff1a;界面 CFG Scale&#xff1a;提示词相关性 denoising&#xff1a;重绘幅度 L2&#xff1a;文生图 女性常用的负面词 nsfw,NSFW,(NSFW:2),legs apart, paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, (…

古记事法:Windows 下 16 位汇编环境搭建指南(DOSBox-X 篇)

文章目录 参考环境DOSBox-XWOWWindows On Windows 产生的原因Windows On Windows 的工作原理WOW16 的结束与 WOW64 的未来 在现代操作系统中运行 16 位应用程序DOSBox-X 16 位汇编环境的搭建应用准备挂载自动挂载dosbox-x.conf配置工具 参考 项目描述搜索引擎Bing、GoogleAI 大…

CV面试知识点总结

一.卷积操作和图像处理中的中值滤波操作有什么区别&#xff1f; 1.1卷积操作 卷积操作是一种线性操作&#xff0c;通常用于特征的提取&#xff0c;通过卷积核的加权求和来得到新的像素值。1.2中值滤波 原文&#xff1a; https://blog.csdn.net/weixin_51571728/article/detai…

传输层协议——TCP、UDP

目录 1、UDP 协议&#xff08;用户数据报协议&#xff09; 协议特点 报文首部格式 2、TCP 协议&#xff08;传输控制协议&#xff09; 协议特点 报文首部格式 TCP连接建立时的三次握手 TCP拆除连接的四次挥手 TCP的流量控制 TCP的拥塞控制 3、传输层端口号 三类端口…

1.6.C++项目:仿muduo库实现并发服务器之channel模块的设计

项目完整版在&#xff1a; 文章目录 一、channel模块&#xff1a;事件管理Channel类实现二、提供的功能三、实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;功能设计 四、代码&#xff08;一&#xff09;框架&#xff08;二…

腾讯云服务器哪个配置比较值得?

腾讯云服务器哪款配置比较好值得买&#xff1f;轻量应用服务器性价比值得买&#xff0c;轻量2核2G3M带宽95元一年、2核4G5M带宽218元一年、2核2G4M带宽三年540元一年、4核8G12M配置446元一年、8核16G18M带宽1668元15个月、16核32G28M轻量服务器3468元15个月。腾讯活动入口&…

Git多账号管理通过ssh 公钥的方式,git,gitlab,gitee

按照目前国内访问git&#xff0c;如果不科学上网&#xff0c;我们很大可能访问会超时。基于这个&#xff0c;所以我现在的git 配置已经增加到了3个了 一个公司gitlab&#xff0c;一个git&#xff0c;一个gitee. 以下基于这个环境&#xff0c;我们来说明下如何创建配置ssh公钥。…

多线程 - 单例模式

单例模式 ~~ 单例模式是常见的设计模式之一 什么是设计模式 你知道象棋,五子棋,围棋吗?如果,你想下好围棋,你就不得不了解一个东西,”棋谱”,设计模式好比围棋中的 “棋谱”. 在棋谱里面,大佬们,把一些常见的对局场景,都给推演出来了,照着棋谱来下棋,基本上棋力就不会差到哪…

Git使用【上】

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析3 前言 先前有些git命令我在我的其它文章里面已经写过&#xff0c;若要查看可参考【Linu…

前端面试:01.图中输入什么?

~~~~~~~~~~~~~ 先自行想一想&#xff0c;答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ 先自行想一想&#xff0c;答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ 先自行想一想&#xff0c;答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ 先自行想一想&#xff0c;答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~…

基于SSM的视频点播系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

接口自动化之测试数据动态生成并替换

一、测试数据 1. 随机库random 查看内置random方法&#xff0c;该方法自行学习&#xff0c;不再介绍。 show 2. Faker库 pip install faker showHttps://github.com/joke2k/faker 3. 应用到项目中 3.1 思路 在用例数据中添加标志位&#xff0c;设计这个标志位为 {{特…

【STM32基础 CubeMX】ADC的基础使用

文章目录 前言一、ADC是什么二、使用CubeMX配置ADC三、代码分析3.1 cubemx生成代码分析3.2 ADC HAL库函数HAL_ADC_Start_IT开启adc中断函数获取ADC值 四、示例代码&#xff1a;获取光敏电阻的值总结 前言 在嵌入式系统开发中&#xff0c;STM32系列微控制器是广泛应用的一种硬件…

【分布式云储存】Springboot微服务接入MinIO实现文件服务

文章目录 前言技术回顾准备工作申请accessKey\secretKey创建数据存储桶公共资源直接访问测试 接入springboot实现文件服务依赖引入配置文件MinIO配置MinIO工具类 OkHttpSSLSocketClient兼容ssl静态资源预览解决方案资源上传预览测试测试结果 前言 上篇博客我们介绍了分布式云存…

UCOS的任务创建和删除

一、任务创建和删除的API函数 1、任务创建和删除本质就是调用uC/OS的函数 API函数 描述 OSTaskCreate() 创建任务 OSTaskDel() 删除任务 注意&#xff1a; 1&#xff0c;使用OSTaskCreate() 创建任务&#xff0c;任务的任务控制块以及任务栈空间所需的内存&#xff0c…

【云备份项目】:环境搭建(g++、json库、bundle库、httplib库)

文章目录 1. g 升级到 7.3 版本2. 安装 jsoncpp 库3. 下载 bundle 数据压缩库4. 下载 httplib 库从 Win 传输文件到 Linux解压缩 1. g 升级到 7.3 版本 &#x1f517;链接跳转 2. 安装 jsoncpp 库 &#x1f517;链接跳转 3. 下载 bundle 数据压缩库 安装 git 工具 sudo yum…

Linux性能优化--性能工具-系统CPU

2.0.概述 本章概述了系统级的Linux性能工具。这些工具是你追踪性能问题时的第一道防线。它们能展示整个系统的性能情况和哪些部分表现不好。 1.理解系统级性能的基本指标&#xff0c;包括CPU的使用情况。 2.明白哪些工具可以检索这些系统级性能指标。 2.1CPU性能统计信息 为…