I asynchronously invoke method with Spring, using @Async.This method invokes other method annotated with @PreAuthorize, Spring Security Annotation. To make authorization works I have to set SecurityContextHolder
mode to MODE_INHERITABLETHREADLOCAL
, so that authentication info is passed to the asynchronous call. Everything works fine so far.
However when I logout and login as a different user, in asynchronous method SecurityContextHolder stores authentication info of the old user, that has bee logged out. It causes of course unwanted AccessDenied
exception. There is no such problem with synchronous calls.
I have defined <task:executor id="executors" pool-size="10"/>
, so may it be a problem that once thread in executors pool has been initialized it will not override authentication information?
Never use @Async on top of a private method. In runtime, it will not able to create a proxy and, therefore, not work. 3. Never write an Async method in the same class where the caller method invokes the same Async methodAsync method in the same class where the caller method invokes the same Async method.
Simply put, annotating a method of a bean with @Async will make it execute in a separate thread. In other words, the caller will not wait for the completion of the called method. One interesting aspect in Spring is that the event support in the framework also has support for async processing if necessary.
Spring Boot uses a SimpleAsyncTaskExector to run an async method. This Executor runs by default and it can be overridden at two levels- at the individual method levels or at the application level.
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.
I guess MODE_INHERITABLETHREADLOCAL
doesn't work correctly with thread pool.
As a possible solution you can try to subclass ThreadPoolTaskExecutor
and override its methods to propagate SecurityContext
manually, and then declare that executor instead of <task:executor>
, something like this:
public void execute(final Runnable r) { final Authentication a = SecurityContextHolder.getContext().getAuthentication(); super.execute(new Runnable() { public void run() { try { SecurityContext ctx = SecurityContextHolder.createEmptyContext(); ctx.setAuthentication(a); SecurityContextHolder.setContext(ctx); r.run(); } finally { SecurityContextHolder.clearContext(); } } }); }
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