PROBLEM
Let’s assume we have the following Parent
class…
@Service class Parent { @Autowired Child child void run() { println "Parent: ${SecurityContextHolder.context.authentication?.principal}" child.run() println "Parent: Done" } }
… and Child
class…
@Service class Child { @Async void run() { Thread.sleep(500) println "Child: ${SecurityContextHolder.context.authentication?.principal}" } }
Let’s also assume the user has successfully logged in and Spring Security has set up the user authentication info.
The Parent
will spawn a new thread (through @Async
) to run Child
.
When invoking the Parent
, this is what we see:-
Parent: USER_PRINCIPAL Parent: Done Child: null
The Child
, for some reason, doesn’t get the receive the user authentication info.
SOLUTION
By default, SecurityContextHolder
uses MODE_THREADLOCAL
to store the user authentication info. As a result, this info is not accessible to methods outside the current execution thread.
To fix this, configure SecurityContextHolder
to use MODE_INHERITABLETHREADLOCAL
to pass the user authentication info to other spawned threads.
@Configuration @EnableAsync class AppConfig { AppConfig() { SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL) } }
When invoking the Parent
again, now the Child
will also receive the user authentication object:-
Parent: USER_PRINCIPAL Parent: Done Child: USER_PRINCIPAL