Spring Security中的SecurityContext和SecurityContextHolder

SecurityContext 和 SecurityContextHolder 是 Spring Security 的两个基本类。

  1. SecurityContext 用于存储当前已验证用户的详细信息,也称为principle。因此,如果要获取用户名或任何其他用户详细信息,首先需要获取 SecurityContext。
  2. SecurityContextHolder 是一个辅助类,用于访问安全上下文。默认情况下,它使用 ThreadLocal 对象来存储安全上下文,这意味着即使不传递 SecurityContext 对象,同一执行线程中的方法也始终可以使用安全上下文。不过不用担心网络应用程序中的 ThreadLocal 内存泄漏,Spring Security 会负责清理 ThreadLocal。

顺便说一下,这并不是 SecurityContextHolder 存储当前 SecurityContext 的唯一方法,它还可以在启动时配置策略,以指定存储上下文的方式。例如,你可以在独立应用程序中使用 SecurityContextHolder.MODE_GLOBAL 策略。

需要学习的关键是如何从 SecurityContextHolder 获取 SecurityContext,然后从中检索当前用户的详细信息?例如,如果想知道当前登录用户的用户名,如何在 Spring Security 中获取?

要获取当前用户名,首先需要一个从 SecurityContextHolder 获取的 SecurityContext。SecurityContext 将用户详细信息保存在一个 Authentication 对象中,可以通过调用 getAuthentication() 方法获取该对象。

获得 Authentication 对象后,可以将其转换为 UserDetails 对象,也可以原样使用。UserDetails 对象是 Spring Security 用来保存用户相关信息的对象。

如何在 Spring Security 中获取当前登录用户名
以下是在 Spring Security 中获取安全上下文并获得当前登录用户名的代码:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 
if (principal instanceof UserDetails) {
  String username = ((UserDetails)principal).getUsername();
} else {
  String username = principal.toString();
}

getContext() 返回的对象是 SecurityContext 接口的一个实例。该对象存储在线程本地存储区中。

在 Spring Security 中,getPrincipal() 方法通常会返回 UserDetails 对象,其中包含当前登录用户的所有详细信息。

总之,如果你仔细观察,就会发现当我们考虑 Spring 和依赖注入时,这并不是一段好代码。因此,如果你需要知道当前登录用户的详细信息,例如在 Spring MVC 控制器中,我建议你声明一个依赖关系,让 Spring 为你提供 Principal 对象,而不是你去查询它们,从而创建一个紧密耦合的系统。

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();
  }
 
}

或者,也可以要求提供身份验证对象,而不是 Principal 对象,如下图所示:

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();
  }
}


以上就是关于 Spring Security 中什么是安全上下文以及如何从 SecurityContextHolder 类中获取 SecurityContext 的全部内容。这些都是一些基本类,因此你必须熟悉它们。

存储部分(即 SecurityContext 存储在 ThreadLocal 中)是可选的,但了解细节也很有好处。请记住,如果需要用户详细信息(如用户名等),最好在 Spring MVC 控制器中请求 Principal 或 Authentication 对象,而不是使用 SecurityContextHolder 来获取。