Spring Security异步无法获取用户认证信息的解决方法

 更新时间:2024年09月14日 09:31:13   作者:忆昔年.  
最近使用 Springboot 中 @Async 注解异步调用方法时,发现无法获取到用户认证信息,本文小编给大家介绍了Spring Security异步无法获取用户认证信息的原因和解决方法,并通过代码示例介绍的非常详细,需要的朋友可以参考下

原因:

Spring Security中的上下文SecurityContext的管理策略有三种

public class SecurityContextHolder {
    public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";

    public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";

    public static final String MODE_GLOBAL = "MODE_GLOBAL";
}

默认的管理策略是:MODE_THREADLOCAL,对应的策略类是
org.springframework.security.core.context.ThreadLocalSecurityContextHolderStrategy

以下是 Spring Security 初始化策略的代码:

public class SecurityContextHolder {
    public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";

    public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";

    public static final String MODE_GLOBAL = "MODE_GLOBAL";
    // 获取系统配置的策略
    private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
    // 预初始化策略
    private static SecurityContextHolderStrategy strategy;
    
    private static void initializeStrategy() {
        if (MODE_PRE_INITIALIZED.equals(strategyName)) {
            Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED
                    + ", setContextHolderStrategy must be called with the fully constructed strategy");
            return;
        }
        if (!StringUtils.hasText(strategyName)) {
            // Set default
            strategyName = MODE_THREADLOCAL;
        }
        if (strategyName.equals(MODE_THREADLOCAL)) {
            strategy = new ThreadLocalSecurityContextHolderStrategy();
            return;
        }
        if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
            strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
            return;
        }
        if (strategyName.equals(MODE_GLOBAL)) {
            strategy = new GlobalSecurityContextHolderStrategy();
            return;
        }
        // Try to load a custom strategy
        try {
            Class<?> clazz = Class.forName(strategyName);
            Constructor<?> customStrategy = clazz.getConstructor();
            strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
        }
        catch (Exception ex) {
            ReflectionUtils.handleReflectionException(ex);
        }
    }
}

ThreadLocalSecurityContextHolderStrategy 里保存Context的方式是 ThreadLocal,ThreadLocal 子线程并不能获取父类线程的 ThreadLocalMap,
所以就导致了异步无法获取到用户认证信息。

final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {

    private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new ThreadLocal<>();

}

解决方法:

更改上下文的管理策略 SecurityContextHolderStrategy 为 MODE_INHERITABLETHREADLOCAL,即

InheritableThreadLocalSecurityContextHolderStrategy,里面保存上下文的方式是 InheritableThreadLocal,可以获取父线程的 ThreadLocalMap。

final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {

	private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new InheritableThreadLocal<>();

}

InheritableThreadLocal可以获取到子线程信息‌,主要是因为它是ThreadLocal的一个子类,设计用来在父子线程间传递数据。
ThreadLocal变量通常只在当前线程中存在,并且每个线程都有其自己的ThreadLocalMap,
这样保证了线程间的数据隔离。然而,在某些情况下,我们需要父线程中的数据能够在由父线程创建的子线程中被访问,
这时就需要使用InheritableThreadLocal。
InheritableThreadLocal的实现机制使其能够在子线程中访问父线程的数据。具体来说,当父线程创建一个子线程时,
InheritableThreadLocal会采取措施确保子线程可以访问到父线程中设置的InheritableThreadLocal变量。
这通常涉及到在子线程的Thread对象中复制父线程的ThreadLocalMap,从而使子线程能够访问到父线程中设置的变量。
这种机制允许在多线程环境中,特别是在使用线程池等场景下,实现父子线程间数据的传递和共享。
需要注意的是,虽然InheritableThreadLocal提供了在子线程中访问父线程数据的能力,但它并不适用于所有情况,
特别是在复杂的线程交互中可能需要更精细的控制。此外,使用InheritableThreadLocal时也需要注意避免内存泄漏等问题,因为如果不当使用,
它可能会导致资源的不当占用‌。

代码:

  • 1、可以在配置 Security 的配置类中添加 Bean
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
        factoryBean.setTargetClass(SecurityContextHolder.class);
        factoryBean.setTargetMethod("setStrategyName");
        factoryBean.setArguments(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
        return factoryBean;
    }
}
  • 2、可以在配置 Security 的配置类中添加类初始化方式,会在注入 SecurityContextHolder 之后调用其中静态方法更改 SecurityContextHolderStrategy 的实现类
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @PostContruct
    public void init() {
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }
}
  • 3、可以在启动项加上 -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL 参数,启动时更改 SecurityContextHolderStrategy 的实现类

以上就是Spring Security异步无法获取用户认证信息的解决方法的详细内容,更多关于Spring Security异步无法获取信息的资料请关注脚本之家其它相关文章!

相关文章

  • java集合中list的用法代码示例

    java集合中list的用法代码示例

    这篇文章主要介绍了java集合中list的用法代码示例,分享了相关代码,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Java线程变量ThreadLocal详细解读

    Java线程变量ThreadLocal详细解读

    这篇文章主要介绍了Java线程变量ThreadLocal详细解读,多线程访问同一个变量的时候,很容易出现问题,特别是多线程对一个共享变量进行写入的时候,为了线程的安全在进行数据写入时候会进行数据的同步,需要的朋友可以参考下
    2024-01-01
  • 详解Java中的ReentrantLock锁

    详解Java中的ReentrantLock锁

    这篇文章主要介绍了Java中ReentrantLock锁的相关资料,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-01-01
  • 浅谈JVM之java class文件的密码本

    浅谈JVM之java class文件的密码本

    一切的一切都是从javac开始的。从那一刻开始,java文件就从我们肉眼可分辨的文本文件,变成了冷冰冰的二进制文件。变成了二进制文件是不是意味着我们无法再深入的去了解java class文件了呢?答案是否定的。本文将详细介绍JVM之java class文件的密码本。
    2021-06-06
  • Spring Boot 和 Spring 到底有啥区别你知道吗

    Spring Boot 和 Spring 到底有啥区别你知道吗

    Spring Boot框架的核心就是自动配置,只要存在相应的jar包,Spring就帮我们自动配置。接下来通过本文给大家介绍Spring与Spring boot的区别介绍,非常不错,需要的朋友参考下吧
    2021-08-08
  • uploadify上传及后台文件合法性验证的代码解析

    uploadify上传及后台文件合法性验证的代码解析

    这篇文章主要介绍了uploadify上传及后台文件合法性验证的代码解析,整段代码分为后台上传方法,文件合法性验证类,前端上传js,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • Springboot整合Shiro之加盐MD5加密的方法

    Springboot整合Shiro之加盐MD5加密的方法

    这篇文章主要介绍了Springboot整合Shiro之加盐MD5加密的方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-12-12
  • Java util concurrent及基本线程原理简介

    Java util concurrent及基本线程原理简介

    这篇文章主要介绍了Java util concurrent及基本线程原理简介,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Javaweb开发中通过Servlet生成验证码图片

    Javaweb开发中通过Servlet生成验证码图片

    这篇文章主要为大家详细介绍了Javaweb开发中通过Servlet生成验证码图片的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • java map中相同的key保存多个value值方式

    java map中相同的key保存多个value值方式

    这篇文章主要介绍了java map中相同的key保存多个value值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08

最新评论