Reputation: 1034
I use Async with a method that call a remote service with Feign and I need to append an oauth2 token to the request, for that I use a 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());
}
};
}
But when the requestInterceptor is used in another thread, I don't have acces to the same security context so getAuhentication return null.
I try to fix it in the executor configuration, I create a DelegatingSecurityContextExecutor wrapping the executor and the security context. But it seems that the bean is created in the 'main' thread and the security context is not the same used then, when a RestController method is executed, so the getAuthentication() still return 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;
}
How can I configure the executor the right way ?
Upvotes: 5
Views: 3909
Reputation: 1034
I finally found the solution, it is possible to propagate the security context automaticly to the other threads.
Just add this line of code in the static main method of your spring boot application :
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
The solution is well explained here : https://www.baeldung.com/spring-security-async-principal-propagation?fbclid=IwAR1zeGKvRvBb7GG8SmxO4x8-NlKkG39Q29WoLKxZ8NRzyKEcnDWx4Q6EUk0
!! WARNING !! : I noticed an unexpected behaviour with that solution, at least on my local dev environment. I'm connected to my app with two different accounts using sessionbox tool of chrome (same with different browsers), and it seems that when I 'm connected with user A SecurityContextHolder.getContext().getAuthentication().getPrincipal() return the security context of user B ... So Huge security problem ! I'm looking for a solution at the moment.
Reading this post : How to set up Spring Security SecurityContextHolder strategy? the solution seems to be here Spring Security and @Async (Authenticated Users mixed up)
Upvotes: 8
Reputation: 1895
It seems to me you can not use RequestInterceptor
here. As far as I know when you use @Async
you lose request context in the method which you want to execute in asynchronous way. To do so you have to explicitly pass access token to asynchronous method and provide it as request header:
@FeignClient(name = "userClient", url ="${userService.hostname}")
public interface MyFeignClient {
String AUTH_TOKEN = “Authorization”;
@GetMapping(“/users”)
List<User> findUsers(@RequestHeader(AUTH_TOKEN) String bearerToken);
}
Upvotes: 0