如何在使用 Async 创建的线程中访问安全上下文

Kai*_*kun 2 multithreading asynchronous spring-mvc spring-boot spring-cloud-feign

我将 Async 与使用 Feign 调用远程服务的方法一起使用,我需要将 oauth2 令牌附加到请求中,为此我使用了 RequestInterceptor。

@Bean
public RequestInterceptor requestTokenBearerInterceptor() {

    return requestTemplate -> {

        Object principal = SecurityContextHolder
                .getContext()
                .getAuthentication()
                .getPrincipal();

        if (!principal.equals("anonymousUser")) {

            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
                    SecurityContextHolder.getContext().getAuthentication().getDetails();

            requestTemplate.header("Authorization", "bearer " + details.getTokenValue());
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

但是当 requestInterceptor 在另一个线程中使用时,我无法访问相同的安全上下文,因此 getAuthentication 返回 null。

我尝试在执行程序配置中修复它,我创建了一个 DelegatingSecurityContextExecutor 来包装执行程序和安全上下文。但似乎 bean 是在“主”线程中创建的,并且安全上下文与当时使用的安全上下文不同,当执行 RestController 方法时,因此 getAuthentication() 仍然返回 null。

   @Bean(name = "asyncExecutor")

    public Executor asyncExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("AsynchThread-");
        executor.initialize();

        Executor wrappedExecutor = new DelegatingSecurityContextExecutor(executor, SecurityContextHolder.getContext());

        return wrappedExecutor;
    }
Run Code Online (Sandbox Code Playgroud)

如何以正确的方式配置执行程序?

Kai*_*kun 5

我终于找到了解决方案,可以将安全上下文自动传播到其他线程。

只需在 Spring Boot 应用程序的静态 main 方法中添加这行代码:

 SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
Run Code Online (Sandbox Code Playgroud)

解决方案在这里得到了很好的解释:https : //www.baeldung.com/spring-security-async-principal-propagation?fbclid=IwAR1zeGKvRvBb7GG8SmxO4x8-NlKkG39Q29WoLKxZ8NRzyKEcnDWx4Q6EUk0

!! 警告 !!:我注意到该解决方案的意外行为,至少在我的本地开发环境中。我使用 chrome 的 sessionbox 工具通过两个不同的帐户连接到我的应用程序(与不同的浏览器相同),似乎当我与用户 A SecurityContextHolder.getContext().getAuthentication().getPrincipal() 连接时返回用户 B 的安全上下文......如此巨大的安全问题!我目前正在寻找解决方案。

阅读这篇文章:如何设置 Spring Security SecurityContextHolder 策略?解决方案似乎在这里Spring Security 和 @Async(经过身份验证的用户混淆)