Monday, July 3, 2023

What is SecurityContext and SecurityContextHolder in Spring Security? Example Tutorial

The SecurityContext and SecurityContextHolder are two fundamental classes of Spring Security. 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. 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. Don't worry about the ThreadLocal memory leak in web application though, Spring Security takes care of cleaning ThreadLocal.

Btw, that's not the only way a SecurityContextHolder can store current SecurityContext, it can be configured with a strategy on startup to specify how you would the context to be stored. For example, you can use SecurityContextHolder.MODE_GLOBAL strategy for a standalone application.

The key thing to learn is how do you get the SecurityContext from the SecurityContextHolder? and then retrieving current user details from that? For example, if you want to know the username of the currently logged-in user then how do you get that in Spring security?

In order to get the current username, you first need a SecurityContext, which is obtained from SecurityContextHolder. This SecurityContext keep the user details in an Authentication object, which can be obtained by calling the getAuthentication() method.

Once you got the Authentication object, you can either cast it into UserDetails or use it as it is. The UserDetails object is the one Spring Security uses to keep user-related information.



How to get the current logged-in Username in Spring Security

Here is the code to get the security context in Spring security and obtain the name of the currently logged-in user:

Object principal = SecurityContextHolder.getContext()
                                        .getAuthentication()
                                        .getPrincipal();

if (principal instanceof UserDetails) {
  String username = ((UserDetails)principal).getUsername();
} else {
  String username = principal.toString();
}

The object returned by getContext() is an instance of the SecurityContext interface. This is the object that is stored in thread-local storage.

The getPrincipal() method normally returns the UserDetails object in Spring Security, which contains all the details of the currently logged-in user. 


What is spring spring context and filter


Anyway, if you look closer, you will find that this is not really a nice code when we think about Spring and dependency injection. So if you ever need to know current logged-in user details like,  in Spring MVC controller, I suggest you declare a dependency and let Spring provide you the Principal object, rather you querying for them and create a tightly coupled system.


Here is an example of that

import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MVCController {

  @RequestMapping(value = "/username", method = RequestMethod.GET)
  @ResponseBody
  public String currentUserName(Principal principal) {
     return principal.getName();
  }

}
Alternatively, you can also ask for an Authentication object instead of a Principal object as shown below:

import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SpringMVCController {

  @RequestMapping(value = "/username", method = RequestMethod.GET)
  @ResponseBody
  public String currentUserName(Authentication authentication) {
     return authentication.getName();
  }
}

If you want to know more ways, you can also see my post about 3 ways to get the current username in Spring Security, where I have discussed a couple of more ways to retrieve the current username in the Spring MVC controller.

That's all about what is security context in Spring security and how you can obtain a SecurityContext from SecurityContextHolder class. These are some of the fundamental classes, hence you must be familiar with them.

The storage part i.e. SecurityContext is stored in ThreadLocal is optional, but it's also good to know the detail. Just remember, if you ever need user details e.g. username etc, you better ask for Principal or Authentication object in Spring MVC controller, rather than using SecurityContextHolder to obtain them.

Other Spring Security Articles and Resources you may like:
  • 15 Spring Boot Interview Questions with Answers (questions)
  • Top 5 Courses to learn Spring in depth (courses)
  • Official Spring Security Documentation (docs)
  • Top 5 Spring Boot Features Java developer should know (features)
  • 10 Free Courses to learn Spring Boot for Beginners (free courses)
  • 21+ Spring MVC Interview Questions for Java developers (Questions)
  • Top 5 Courses to learn Microservices in Java (courses)
  • How to Crack Spring Certification for Java developers (certification)
  • 10 Advanced Spring Boot Courses for Java developers (courses)
  • How to get a ServletContext object in the Spring controller? (example)
  • Top 5 Courses to learn Spring Boot in-depth (courses)
  • How to change the port of tomcat in Spring boot (tutorial)
  • Top 5 Books to learn Spring Boot and Spring Cloud (books)
  • What is the default bean scope in the Spring MVC framework? (answer)
  • Top 5 Courses to learn Spring Cloud for Java developers (courses)
  • 5 Best Resources for Spring Certification (resources) 

6 comments:

  1. Thank you Priya, glad you liked my blog.

    ReplyDelete
  2. You got your principle wrong. The currently authenticated user is a "principal", not a "principle".

    ReplyDelete
  3. Hey, great article. Thought it might be worth mentioning the Spring Guru course doesn't cover Spring Security in depth.

    ReplyDelete
  4. Thank You for an excellent and updated series.

    ReplyDelete