Spring – security context management
Once the Authentication Filter intercept the authentication request, the details about the user that has been authenticated are stored in the security context.
The security context represent the instance storing the Authentication Object, that is been later accessed by the Controllers.
The context is managed by a SecurityContextHolder, with one of the following three strategies:
- MODE_THREADLOCAL – each request has its on thread (own sec. context)
- MODE_INHERITABLETHREADLOCAL – security context copied to next thread (@Async methods inheriting the context)
- MODE_GLOBAL – same context for all the threads (not thread safe!)
You can get the authentication object from the SecurityContextHolder:
SecurityContext context = SecurityContextHolder.getContext(); Authentication a = context.getAuthentication();
If you want to specify a different strategy than threadlocal, you have to set the strategy in the configuration, for example:
@Configuration @EnableAsync public class SecurityConfig{ //other lines of code @Bean public InitializingBean initializeSecurityContextHolder() { return () -> SecurityContextHolder.setStrategyName( SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); } }
If the new threads are not created by the Spring framework, you need to implement the security context propagation to the new thread on your own.
To solve the issue you can use a DelegatingSecurityContextRunnable
or DelegatingSecurityContextCallable, if you need to return a value, to copy the context to the next thread “manually”.
Instead of simply using an ExecutorService, you can use its decorator provided by Spring security, called “DelegatingSecurityContextExecutorService
“to propagate the thread:
@GetMapping("/hello") public String hello() throws Exception { Callable<String> task = () -> { SecurityContext context = SecurityContextHolder.getContext(); return context.getAuthentication().getName(); }; ExecutorService e = Executors.newCachedThreadPool(); e = new DelegatingSecurityContextExecutorService(e); try { return "HEllo, " + e.submit(task).get() + "!"; } finally { e.shutdown(); } }