Spring Security中的SecurityContext和SecurityContextHolder

banq發表於2024-02-14

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 = <font>"/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 = <font>"/username", method = RequestMethod.GET)
  @ResponseBody
  public String currentUserName(Authentication authentication) {
     return authentication.getName();
  }
}


以上就是關於 Spring Security 中什麼是安全上下文以及如何從 SecurityContextHolder 類中獲取 SecurityContext 的全部內容。這些都是一些基本類,因此你必須熟悉它們。

儲存部分(即 SecurityContext 儲存在 ThreadLocal 中)是可選的,但瞭解細節也很有好處。請記住,如果需要使用者詳細資訊(如使用者名稱等),最好在 Spring MVC 控制器中請求 Principal 或 Authentication 物件,而不是使用 SecurityContextHolder 來獲取。

 

相關文章