Java21 + SpringBoot3使用Spring Security时如何在子线程中获取到认证信息

目录

前言

近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。本项目为前后端分离开发,后端基于Java21SpringBoot3开发,后端使用Spring SecurityJWTSpring Data JPA等技术栈,前端提供了vueangularreactuniapp微信小程序等多种脚手架工程。

项目地址:https://gitee.com/breezefaith/fast-alden

在使用Spring Security时,笔者定义了一个ThreadPoolTaskExecutorBean用于创建子线程,但是却遇到了子线程中无法获取到认证信息的问题,本文主要介绍该问题的解决方案。

原因分析

Spring Security中想要获取登录用户信息,只能在当前线程中获取,不能在子线程中获取,其中一个重要的原因就是SecurityContextHolder默认将用户信息保存在ThreadLocal中。

ThreadLocal叫做本地线程变量,意思是说,ThreadLocal中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。

解决方案

方案1:手动设置线程中的认证信息

在子线程的业务逻辑代码之前先手动设置认证信息,后续就可以通过SecurityContextHolder直接获取。

java 复制代码
// 获取当前线程认证信息
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是一致的,都是先获取到当前线程的认证信息,然后传递给新线程。

java 复制代码
// 使用DelegatingSecurityContextRunnable创建线程
Runnable runnable = new DelegatingSecurityContextRunnable(() -> {
  // 线程处理逻辑
  // ...
});
new Thread(runnable).start();

方案3:修改Spring Security安全策略

默认情况下,Spring Security使用ThreadLocal存储认证信息,但实际上它也支持通过设置安全策略来修改认证信息的存储位置,它支持三种安全策略,有MODE_THREADLOCALMODE_INHERITABLETHREADLOCALMODE_GLOBAL。如果没有指定,则会默认使用MODE_THREADLOCAL策略。

  • MODE_THREALOCAL表示用户信息只能由当前线程访问。
  • MODE_INHERITABLETHREADLOCAL表示用户信息可以由当前线程及其子线程访问.。
  • MODE_GLOBAL表示用户信息没有线程限制,全局都可以访问,一般用于gui的开发中。

因此,将安全策略修改为MODE_INHERITABLETHREADLOCAL就可以在子线程中获取到认证信息。

Spring Security还提供了两种修改安全策略的方式,一种是通过设置JVM参数spring.security.strategy,一种是调用SecurityContextHoldersetStrategyName方法。

通过设置JVM参数修改安全策略

bash 复制代码
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

通过SecurityContextHolder修改安全策略

可以借助@PostConstruct注解在程序启动后修改安全策略。

java 复制代码
@Configuration
public class CommonSecurityConfig {
    @PostConstruct
    public void setStrategyName() {
        // 程序启动后修改认证信息上下文存储策略,支持子线程中获取认证信息
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }
}

@PostConstruct注解是由Java提供的,它用来修饰一个非静态的void方法。它会在服务器加载Servlet的时候运行,并且只运行一次。

总结

本文主要介绍SpringBoot3使用Spring Security时如何在子线程中获取到认证信息。如有错误,还望批评指正。

在后续实践中我也是及时更新自己的学习心得和经验总结,希望与诸位看官一起进步。

相关推荐
IT_102432 分钟前
springboot从零入门之接口测试!
java·开发语言·spring boot·后端·spring·lua
湖北二师的咸鱼1 小时前
c#和c++区别
java·c++·c#
weixin_418007601 小时前
软件工程的实践
java
陌殇殇2 小时前
Hadoop 002 — HDFS常用命令及SpringBoot整合操作
hadoop·spring boot·hdfs
lpfasd1232 小时前
备忘录模式(Memento Pattern)
java·设计模式·备忘录模式
迢迢星万里灬2 小时前
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术点解析
java·spring boot·spring·mybatis·spring mvc·面试指南
代码丰2 小时前
使用Spring Cloud Stream 模拟生产者消费者group destination的介绍(整合rabbitMQ)
java·分布式·后端·rabbitmq
weixin_446122462 小时前
浏览器播放监控画面
java·spring boot
lpfasd1232 小时前
模板方法模式(Template Method Pattern)
java·开发语言·设计模式·模板方法模式
hn小菜鸡3 小时前
LeetCode 2529.正整数和负整数的最大计数
java·算法·leetcode