Skip to content
Advertisement

Isn’t SecurityContextHolder a Bean?

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?

Advertisement

Answer

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 uses TreadLocal 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 on TreadLocal, 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 the SecurityContext. securitycontextholder

enter image description here

The SecurityContextHolder is where Spring Security stores the details of who is authenticated. Spring Security does not care how the SecurityContextHolder 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());
    }
}
Advertisement