This is my scenario:
My app has Mongo Auditing enabled, with a custom AuditorAware which gets the current user from the SecurityContext
. This works well with synchronous methods, and the current auditor is successfully saved, but I can't make it work properly with @Async
methods.
I have an async method (CompletableFuture
) that makes some updates on my Mongo Database. When the AuditorAware.getCurrentAuditor()
is called, no authentication info exists, and I can't get the current auditor (SecurityContextHolder.getContext().getAuthentication()
returns null
).
@Override
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()
|| authentication instanceof AnonymousAuthenticationToken) {
log.error("Not authenticated");
return null;
}
[...]
}
I'm using a DelegatingSecurityContextAsyncTaskExecutor
:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(200);
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new ItacaExceptionHandler();
}
}
How can I make it work properly?
Spring security context is always bound to Threadlocal.
Probabably you may to additionally set MODE_INHERITABLETHREADLOCAL for the security context.
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
http://www.ogrigas.eu/spring/2010/04/inherit-spring-security-context-in-child-threads
How to set up Spring Security SecurityContextHolder strategy?
Following the comments on kuhajeyan's answer, it appears you are not properly using CompletableFuture
with Spring @Async
.
If you launch your tasks by using e.g. CompletableFuture.supplyAsync(Supplier)
, they will be executed by the common ForkJoinPool
and not the one you have configured for @Async
. You could use the overloads that take an Executor
as argument, but it would not actually benefit from the advantages of @Async
.
What you should do, instead, is let Spring handle the task execution, and simply return a completed CompletableFuture
like this:
@Async
public CompletableFuture<String> someMethod() {
// do some computation, but return a completed future
return CompletableFuture.completedFuture("Hello World!");
}
Spring will then execute your method asynchronously in the configured executor while immediately return a CompletableFuture
which will be completed when your method returns.
If you are using Spring 4.2 or above, this is supported out of the box. Otherwise there is a bit of implementation required, but that would be for another question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With