Java21 + SpringBoot3使用Spring Security时如何在子线程中获取到认证信息 - 程序员偏...
source link: https://www.cnblogs.com/breezefaith/p/18018935
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Java21 + SpringBoot3使用Spring Security时如何在子线程中获取到认证信息
近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。本项目为前后端分离开发,后端基于Java21
和SpringBoot3
开发,后端使用Spring Security
、JWT
、Spring Data JPA
等技术栈,前端提供了vue
、angular
、react
、uniapp
、微信小程序
等多种脚手架工程。
项目地址:https://gitee.com/breezefaith/fast-alden
在使用Spring Security
时,笔者定义了一个ThreadPoolTaskExecutor
Bean用于创建子线程,但是却遇到了子线程中无法获取到认证信息的问题,本文主要介绍该问题的解决方案。
在 Spring Security
中想要获取登录用户信息,只能在当前线程中获取,不能在子线程中获取,其中一个重要的原因就是SecurityContextHolder
默认将用户信息保存在ThreadLocal
中。
ThreadLocal
叫做本地线程变量
,意思是说,ThreadLocal
中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal
为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。
方案1:手动设置线程中的认证信息
在子线程的业务逻辑代码之前先手动设置认证信息,后续就可以通过SecurityContextHolder
直接获取。
// 获取当前线程认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 创建新线程
Runnable runnable = new Runnable() {
public void run() {
// 手动设置线程中的认证信息
SecurityContextHolder.getContext().setAuthentication(authentication);
// 线程处理逻辑(后续就能获取到认证信息)
// ...
}
};
new Thread(runnable).start();
方案2:使用DelegatingSecurityContextRunnable
创建线程
Spring Security
考虑到了新线程需要访问认证信息的情况,提供了DelegatingSecurityContextRunnable
类,通过该类构建新线程(返回一个Runnable
对象),线程内部自然能获取认证信息。有兴趣的读者可以阅读一下DelegatingSecurityContextRunnable
的源码,其思路与方法1是一致的,都是先获取到当前线程的认证信息,然后传递给新线程。
// 使用DelegatingSecurityContextRunnable创建线程
Runnable runnable = new DelegatingSecurityContextRunnable(() -> {
// 线程处理逻辑
// ...
});
new Thread(runnable).start();
方案3:修改Spring Security
安全策略
默认情况下,Spring Security
使用ThreadLocal
存储认证信息,但实际上它也支持通过设置安全策略来修改认证信息的存储位置,它支持三种安全策略,有MODE_THREADLOCAL
、MODE_INHERITABLETHREADLOCAL
和MODE_GLOBAL
。如果没有指定,则会默认使用MODE_THREADLOCAL
策略。
MODE_THREALOCAL
表示用户信息只能由当前线程访问。MODE_INHERITABLETHREADLOCAL
表示用户信息可以由当前线程及其子线程访问.。MODE_GLOBAL
表示用户信息没有线程限制,全局都可以访问,一般用于gui的开发中。
因此,将安全策略修改为MODE_INHERITABLETHREADLOCAL
就可以在子线程中获取到认证信息。
Spring Security
还提供了两种修改安全策略的方式,一种是通过设置JVM参数spring.security.strategy
,一种是调用SecurityContextHolder
的setStrategyName
方法。
通过设置JVM参数修改安全策略
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
通过SecurityContextHolder
修改安全策略
可以借助@PostConstruct
注解在程序启动后修改安全策略。
@Configuration
public class CommonSecurityConfig {
@PostConstruct
public void setStrategyName() {
// 程序启动后修改认证信息上下文存储策略,支持子线程中获取认证信息
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
}
@PostConstruct
注解是由Java提供的,它用来修饰一个非静态的void方法。它会在服务器加载Servlet的时候运行,并且只运行一次。
本文主要介绍SpringBoot3
使用Spring Security
时如何在子线程中获取到认证信息。如有错误,还望批评指正。
在后续实践中我也是及时更新自己的学习心得和经验总结,希望与诸位看官一起进步。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK