I use SecurityContextHolder
and a custom UserDetailsService
to obtain UserDetails
from SecurityContextHolder
:
Object o = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetailsDTO user = (UserDetailsDTO) o;
I left out the null checks, etc., but that's the idea. I'm using this in an @Around
pointcut of an @Aspect
:
@Around("execution(* user.service.*.*(..))")
public Object audit(ProceedingJoinPoint call) throws Throwable {
// get user id
// add audit row in db
}
Looking at the SecurityContextHolder
class, it uses a ThreadLocal
by default, but the pointcut stuff also seems to have some sort of encapsulated threading logic.
Is it possible that there could be user collision (i.e. access UserA from one session for a UserB audit event in another concurrent session), or possibly a null user altogether.
Is there a better way to obtain the credentials/user profile?
The SecurityContextHolder is a helper class, which provide access to the security context. By default, it uses a ThreadLocal object to store security context, which means that the security context is always available to methods in the same thread of execution, even if you don't pass the SecurityContext object around.
By default the SecurityContextHolder uses a ThreadLocal to store these details, which means that the SecurityContext is always available to methods in the same thread, even if the SecurityContext is not explicitly passed around as an argument to those methods.
The HttpServletRequest.getUserPrincipal() will return the result of SecurityContextHolder.getContext().getAuthentication() . This means it is an Authentication which is typically an instance of UsernamePasswordAuthenticationToken when using username and password based authentication.
The SecurityContext is used to store the details of the currently authenticated user, also known as a principle. So, if you have to get the username or any other user details, you need to get this SecurityContext first. The SecurityContextHolder is a helper class, which provides access to the security context.
Yes, it's thread safe with the default strategy (MODE_THREADLOCAL
) (as long as you don't try to change the strategy on the fly). However, if you want spawned threads to inherit SecurityContext
of the parent thread, you should set MODE_INHERITABLETHREADLOCAL
.
Also aspects don't have any "threading logic", they are executed at the same thread as the adviced method.
in general, ThreadLocal will not be friendly in a global cached thread pool. An ExecutorService's default cached thread pool (Executors.newCachedThreadPool()) will have either the initializing thread's ThreadLocal storage, or an empty one. In this situation, setting MODE_INHERITABLETHREADLOCAL will not change anything, unless the cached threadpool is initialized per request, which would be a pretty bad usage of it.. Make sure any underlying frameworks or libraries are not using the Executors.newCachedThreadPool() to provide thread pooling for you.
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