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

打印 上一主题 下一主题

主题 874|帖子 874|积分 2622

目录

前言

近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。本项目为前后端分离开发,后端基于Java21和SpringBoot3开发,后端使用Spring Security、JWT、Spring Data JPA等技术栈,前端提供了vue、angular、react、uniapp、微信小程序等多种脚手架工程。
项目地址:https://gitee.com/breezefaith/fast-alden
在使用Spring Security时,笔者定义了一个ThreadPoolTaskExecutorBean用于创建子线程,但是却遇到了子线程中无法获取到认证信息的问题,本文主要介绍该问题的解决方案。
原因分析

在 Spring Security中想要获取登录用户信息,只能在当前线程中获取,不能在子线程中获取,其中一个重要的原因就是SecurityContextHolder默认将用户信息保存在ThreadLocal中。
ThreadLocal叫做本地线程变量,意思是说,ThreadLocal中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。
解决方案

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

在子线程的业务逻辑代码之前先手动设置认证信息,后续就可以通过SecurityContextHolder直接获取。
  1. // 获取当前线程认证信息
  2. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  3. // 创建新线程
  4. Runnable runnable = new Runnable() {
  5.         public void run() {
  6.                 // 手动设置线程中的认证信息
  7.                 SecurityContextHolder.getContext().setAuthentication(authentication);
  8.                
  9.                 // 线程处理逻辑(后续就能获取到认证信息)
  10.                 // ...
  11.         }
  12. };
  13. new Thread(runnable).start();
复制代码
方案2:使用DelegatingSecurityContextRunnable创建线程

Spring Security考虑到了新线程需要访问认证信息的情况,提供了DelegatingSecurityContextRunnable类,通过该类构建新线程(返回一个Runnable对象),线程内部自然能获取认证信息。有兴趣的读者可以阅读一下DelegatingSecurityContextRunnable的源码,其思路与方法1是一致的,都是先获取到当前线程的认证信息,然后传递给新线程。
  1. // 使用DelegatingSecurityContextRunnable创建线程
  2. Runnable runnable = new DelegatingSecurityContextRunnable(() -> {
  3.   // 线程处理逻辑
  4.   // ...
  5. });
  6. 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参数修改安全策略
  1. -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
复制代码

通过SecurityContextHolder修改安全策略

可以借助@PostConstruct注解在程序启动后修改安全策略。
  1. @Configuration
  2. public class CommonSecurityConfig {
  3.     @PostConstruct
  4.     public void setStrategyName() {
  5.         // 程序启动后修改认证信息上下文存储策略,支持子线程中获取认证信息
  6.         SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
  7.     }
  8. }
复制代码
@PostConstruct注解是由Java提供的,它用来修饰一个非静态的void方法。它会在服务器加载Servlet的时候运行,并且只运行一次。

总结

本文主要介绍SpringBoot3使用Spring Security时如何在子线程中获取到认证信息。如有错误,还望批评指正。
在后续实践中我也是及时更新自己的学习心得和经验总结,希望与诸位看官一起进步。
出处:https://www.cnblogs.com/breezefaith/p/18018935本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

千千梦丶琪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表