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
Leave a Reply