×
☰ See All Chapters

Accessing Authenticated User Information with SecurityContext

After the authentication process concludes, you often require details about the authenticated entity. Spring Security's SecurityContext serves this purpose, storing the Authentication instance for subsequent requests. The SecurityContext interface encapsulates the Authentication object and provides methods to get and set it. Here's its definition:

public interface SecurityContext extends Serializable {
   Authentication
getAuthentication();
   
void setAuthentication(Authentication authentication);
}

Management Strategies with SecurityContextHolder

Spring Security offers three strategies via SecurityContextHolder for managing SecurityContext instances:

MODE_THREADLOCAL

"The MODE_THREADLOCAL strategy allows each thread to maintain its own instance of the security context. This approach is commonly used in thread-per-request web applications, where each incoming request is handled by an individual thread. As the default strategy for managing the security context, MODE_THREADLOCAL does not require explicit configuration. You can simply retrieve the security context from the holder using the static getContext() method wherever you need it.

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

   
@GetMapping(value = "/demo")
   
public String sayHello() {
       
SecurityContext context = SecurityContextHolder.getContext();
       
Authentication a = context.getAuthentication();
       
return "Hello, " + a.getName() + "!";
   }
}

 

Accessing authentication directly at the endpoint level is more convenient, as Spring can inject it directly into method parameters. This eliminates the need to explicitly reference the SecurityContextHolder class every time. This approach, demonstrated in the following listing, is considered superior.

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

   
@GetMapping("/hello")
   
public String hello(Authentication a) {
       
return "Hello, " + a.getName() + "!";
   }
}

 

MODE_INHERITABLETHREADLOCAL

MODE_INHERITABLETHREADLOCAL is akin to MODE_THREADLOCAL, but it additionally directs Spring Security to copy the security context to subsequent threads, particularly useful in asynchronous methods. Consequently, threads spawned from the original thread, such as those in @Async methods, inherit the security context.

import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

   
@GetMapping(value = "/demo")
   
@Async
   
// When you invoke this endpoint you will not get the response as it is executed in async
   // You can debug and inspect the value or put the logger to check the value
   
public String sayHello() {
       
SecurityContext context = SecurityContextHolder.getContext();
       
Authentication a = context.getAuthentication();
       
return "Hello, " + a.getName() + "!";
   }
}

 

When attempting to access authentication details with MODE_THREADLOCAL in an asynchronous method, it results in a NullPointerException. This occurs because the method executes on a different thread that doesn't inherit the security context. Consequently, the Authorization object is null, leading to the NullPointerException.

To address this issue, you can utilize the MODE_INHERITABLETHREADLOCAL strategy. This strategy ensures that the security context is copied to subsequent threads, including those created in asynchronous methods. You can set this strategy programmatically using SecurityContextHolder.setStrategyName() or via the system property spring.security.strategy. By doing so, the framework ensures that details from the original thread are propagated to the newly created thread in the asynchronous method.

import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.core.context.SecurityContextHolder;

@Configuration
@EnableAsync
public class ApplicationConfig {

   
@Bean
   
public InitializingBean initializingBean() {
       
return () -> SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
   }
}

 

While the MODE_INHERITABLETHREADLOCAL strategy resolves the issue when the framework creates threads, it may not suffice if your code creates threads independently. In such cases, even with the MODE_INHERITABLETHREADLOCAL strategy, you'll encounter the same problem. This occurs because the framework lacks awareness of the threads created by your code.

MODE_GLOBAL

This strategy ensures that all threads within the application share the same instance of the security context. While suitable for standalone applications, it may not be ideal for web servers where requests are managed independently. In backend web applications, it's typically more appropriate to have the security context segregated per request rather than shared across all requests. However, for standalone applications, this strategy can be beneficial. To implement this strategy, you can use SecurityContextHolder.setStrategyName() or set the spring.security.strategy system property. This allows you to specify the desired strategy for managing the security context according to your application's requirements.

import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.core.context.SecurityContextHolder;

@Configuration
@EnableAsync
public class ApplicationConfig {

   
@Bean
   
public InitializingBean initializingBean() {
       
return () -> SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL);
   }
}

 

It's important to note that the SecurityContext is not inherently thread-safe. Therefore, when using the MODE_GLOBAL strategy where all threads within the application can access the SecurityContext object, you must handle concurrent access carefully to prevent race conditions and ensure data integrity.

 


All Chapters
Author