Issue
Trying to Autowire SecurityContextHolder I get error
required a bean of type 'org.springframework.security.core.context.SecurityContextHolder' that could not be found.
Turns out that it is available from any part of the code like
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
How come it isn't a bean and how does it get initialized under the hood? Are there other static utility classes like that available to be consumed from anywhere and how do I find them?
Solution
SecurityContextHolder
is a utility class which declares a bunch of static
methods, which can be accessed using class name.
It doesn't make much sense to instantiate utility classes (their constructors are usually private to emphasize that), and for that reason SecurityContextHolder
is not a Bean in the Context, hence there's no way (and no need) to inject it.
The purpose SecurityContextHolder
to facilitate access to the SecurityContext
, which contains information specific to the currently authenticated user. Also, SecurityContextHolder
offers various strategies for managing SecurityContext
:
MODE_THREADLOCAL
is the default one, that is suitable for so-called servlet-based Web Application, which are using thread-per-request model. In this case, each thread would have its own context and Spring usesTreadLocal
to manage the context.MODE_INHERITABLETHREADLOCAL
is similar to the previous one with one difference - it allows to use multiple threads per request, and if we spawn a new thread it inherits the context of the parent thread.MODE_GLOBAL
- is not appropriate for server use. This strategy is not based onTreadLocal
, every thread is expected to see the same context. And can be used in a standalone application (for instance in a Swing client).
Here's a short quote from the Spring Security documentation (have a closer at this link if you want to learn more):
10.1. SecurityContextHolder
At the heart of Spring Security’s authentication model is the
SecurityContextHolder
. It contains theSecurityContext
. securitycontextholderThe
SecurityContextHolder
is where Spring Security stores the details of who is authenticated. Spring Security does not care how theSecurityContextHolder
is populated. If it contains a value, then it is used as the currently authenticated user....
So, as a recap, the main purpose of SecurityContextHolder
is to manage SecurityContext
. And security context is meant to provide access to the user's Authentication
.
Authentication
represents the currently authenticated user. And since Authentication
a Bean, it can be provided through dependency injection in another Bean, for instance as a method parameter.
Let's have a look at an example. Assume we have a controller which has an endpoint returning an instance of Foo
which requires some user's information from the Authentication
:
@RestController
public class FooController {
@GetMapping("/foo")
Foo getFoo(Authentication a) {
return FooUtils.generateFoo(a);
}
}
It makes use of the generateFoo()
method by passing Authentication
injected a parameter. Method generateFoo()
located in the FooUtils
which is a utility class.
public class FooUtils {
public static Foo generateFoo(Authentication a) {
return new Foo(a.getPrincipal());
}
}
Authentication
can't be injected in the methods of FooUtils
, since it's a not a Bean, therefore it's being handed out by the caller. If we imagine that chain of calls would be longer, i.e. there would be more actions in between the calling endpoint method and FooUtils.generateFoo()
it would not be very convenient to pass around Authentication
through several methods where it's not going to be used.
Instead, can make use of the SecurityContextHolder
and obtain Authentication
right on the spot inside the FooUtils
:
public class FooUtils {
public static Foo generateFoo() {
Authentication a = SecurityContextHolder
.getContext()
.getAuthentication();
return new Foo(a.getPrincipal());
}
}
Answered By - Alexander Ivanchenko
Answer Checked By - Timothy Miller (JavaFixing Admin)