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

问题描述 投票:1回答:2

我使用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在另一个线程中使用时,我无法访问相同的安全上下文,所以getAuhentication返回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
2个回答
1
投票

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

只要在你的spring boot应用程序的静态主方法中添加这行代码。

 SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

这个解决方案在这里有很好的解释。https:/www.baeldung.comspring-security-async-principal-propagation?fbclid=IwAR1zeGKvRvBb7GG8SmxO4x8-NlKkG39Q29WoLKxZ8NRzyKEcnDWx4Q6EUk0

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

阅读这篇文章。如何设置Spring Security SecurityContextHolder策略? 解决方案似乎在这里 Spring安全和@Async(认证用户混为一谈


0
投票

在我看来,你不能用 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);
}
© www.soinside.com 2019 - 2024. All rights reserved.