【问题标题】:How to reach the security context in a thread created with Async如何在使用 Async 创建的线程中访问安全上下文
【发布时间】:2020-09-10 08:34:06
【问题描述】:

我将 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());
        }
    };
}

但是当 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;
    }

如何正确配置执行器?

【问题讨论】:

    标签: multithreading spring-boot spring-mvc asynchronous spring-cloud-feign


    【解决方案1】:

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

    只需在你的 spring boot 应用的静态 main 方法中添加这行代码:

     SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    

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

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

    阅读这篇文章:How to set up Spring Security SecurityContextHolder strategy? 解决方案似乎在这里Spring Security and @Async (Authenticated Users mixed up)

    【讨论】:

    • 好收获!将来会对我有所帮助。接受它作为正确答案
    • @AndrewNepogoda 不客气,我会的,但我必须等两天,因为我回答了我自己的问题。
    【解决方案2】:

    在我看来你不能在这里使用RequestInterceptor。据我所知,当您使用 @Async 时,您会在要以异步方式执行的方法中丢失请求上下文。为此,您必须将访问令牌显式传递给异步方法并将其作为请求标头提供:

    @FeignClient(name = "userClient", url ="${userService.hostname}")
    public interface MyFeignClient {
    
        String AUTH_TOKEN = “Authorization”;
        @GetMapping(“/users”)
        List<User> findUsers(@RequestHeader(AUTH_TOKEN) String bearerToken);
    }
    

    【讨论】:

    • 感谢您的帮助,但可以将安全上下文传播到其他线程
    猜你喜欢
    • 2013-01-27
    • 2014-02-15
    • 1970-01-01
    • 2013-06-03
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多