I'm trying to get the user from spring context in an application spring as follows:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
The problem is that the methods are asynchronous, with annotation @Async:
@Service
@Transactional
public class FooServiceImpl implements FooService {
@Async("asyncExecutor")
public void fooMethod(String bar) {
System.out.println("Foo: " + bar);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
}
}
The problem is that the asynchronous method runs in another thread in another context.
I have tried using a SecurityContextDelegationAsyncTaskExecutor
. The user is propagated to the asynchronous method but if I logout, the user in the asynchronous method is null. This is my code:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(1);
executor.setThreadGroupName("MyCustomExecutor");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setBeanName("asyncExecutor");
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
I have also used a ThreadPoolTaskExecutor
and setting the context of spring security with "MODE_INHERITABLETHREADLOCAL". But the result is the same. The user is not null if I am logged into the application. If I'm not logged the user is null. I really want the user that runs the method, not the current user logged. My code:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(1);
executor.setThreadGroupName("MyCustomExecutor");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setBeanName("asyncExecutor");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
@Configuration
@ComponentScan(basePackageClasses = Application.class, includeFilters = @Filter({Controller.class}), useDefaultFilters = true)
public class MvcConfiguration extends WebMvcConfigurationSupport {
//others beans
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
}
Finally, I found this post . I try with a CustomThreadPoolTaskExecutor overriding execute method. But this method never runs. The method of ThreadPoolTaskExecutor that run is:
<T> Future<T> submit(Callable<T> task)
My code:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
CustomThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor();
executor.setMaxPoolSize(1);
executor.setThreadGroupName("MyCustomExecutor");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setBeanName("asyncExecutor");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final long serialVersionUID = 1L;
@Override
public void execute(final Runnable r) {
final Authentication a = SecurityContextHolder.getContext().getAuthentication();
super.execute(new Runnable() {
@Override
public void run() {
try {
SecurityContext ctx = SecurityContextHolder.createEmptyContext();
ctx.setAuthentication(a);
SecurityContextHolder.setContext(ctx);
r.run();
} finally {
SecurityContextHolder.clearContext();
}
}
});
}
}
My custom executes method never run. What am I doing wrong in the Custom ThreadPoolTaskExecutor? Some other way to get the user that runs an asynchronous method. No the current user of context.
When a asynchronous method is executed, the code runs but nothing happens other than a compiler warning.
The @EnableAsync annotation switches on Spring's ability to run @Async methods in a background thread pool. This class also customizes the Executor by defining a new bean. Here, the method is named taskExecutor , since this is the specific method name for which Spring searches.
@Async annotation must be on the public method. Spring use a proxy for this annotation and it must be public for the proxy to work. Calling the async method from within the same class. It won't work (Method calling like this will bypass proxy).
In Spring Boot we use an asynchronous mechanism with three quick steps.
Maybe this helps to get execute-method called (worked in my case):
@Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
CustomThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor();
executor.setMaxPoolSize(1);
executor.setThreadGroupName("MyCustomExecutor");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setBeanName("asyncExecutor");
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
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