最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Security的基本組件SecurityContextHolder詳解

 更新時間:2025年08月25日 16:39:45   作者:安迪源文  
SecurityContextHolder是SpringSecurity保存用戶安全上下文的核心工具,默認使用ThreadLocal機制,適用于Servlet應用,支持MODE_GLOBAL和MODE_INHERITABLETHREADLOCAL模式,可通過系統(tǒng)屬性或方法配置,自動管理上下文傳遞

Spring Security 中最基本的組件應該是SecurityContextHolder了。

這是一個工具類,只提供一些靜態(tài)方法。這個工具類的目的是用來保存應用程序中當前使用人的安全上下文。

SecurityContextHolder的工作原理

缺省工作模式MODE_THREADLOCAL

我們知道,一個應用同時可能有多個使用者,每個使用者對應不同的安全上下文,那么SecurityContextHolder是怎么保存這些安全上下文的呢 ?

缺省情況下,SecurityContextHolder使用了ThreadLocal機制來保存每個使用者的安全上下文。

這意味著,只要針對某個使用者的邏輯執(zhí)行都是在同一個線程中進行,即使不在各個方法之間以參數(shù)的形式傳遞其安全上下文,各個方法也能通過SecurityContextHolder工具獲取到該安全上下文。

只要在處理完當前使用者的請求之后注意清除ThreadLocal中的安全上下文,這種使用ThreadLocal的方式是很安全的。當然在Spring Security中,這些工作已經(jīng)被Spring Security自動處理,開發(fā)人員不用擔心這一點。

這里提到的SecurityContextHolder基于ThreadLocal的工作方式天然很適合Servlet Web應用,因為缺省情況下根據(jù)Servlet規(guī)范,一個Servlet request的處理不管經(jīng)歷了多少個Filter,自始至終都由同一個線程來完成。

注意 :

這里講的是一個Servlet request的處理不管經(jīng)歷了多少個Filter,自始至終都由同一個線程來完成;而對于同一個使用者的不同Servlet request,它們在服務端被處理時,使用的可不一定是同一個線程(存在由同一個線程處理的可能性但不確保)。

其他工作模式

有一些應用并不適合使用ThreadLocal模式,那么還能不能使用SecurityContextHolder了呢?

答案是可以的。SecurityContextHolder還提供了其他工作模式。

比如有些應用,像Java Swing客戶端應用,它就可能希望JVM中所有的線程使用同一個安全上下文。此時我們可以在啟動階段將SecurityContextHolder配置成全局策略MODE_GLOBAL。

還有其他的一些應用會有自己的線程創(chuàng)建,并且希望這些新建線程也能使用創(chuàng)建者的安全上下文。

這種效果,可以通過將SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略達到。

使用SecurityContextHolder

獲取當前用戶信息

SecurityContextHolder中保存的是當前訪問者的信息。

Spring Security使用一個Authentication對象來表示這個信息。

一般情況下,我們都不需要創(chuàng)建這個對象,在登錄過程中,Spring Security已經(jīng)創(chuàng)建了該對象并幫我們放到了SecurityContextHolder中。

SecurityContextHolder中獲取這個對象也是很簡單的。比如,獲取當前登錄用戶的用戶名,可以這樣 :

// 獲取安全上下文對象,就是那個保存在 ThreadLocal 里面的安全上下文對象
// 總是不為null(如果不存在,則創(chuàng)建一個authentication屬性為null的empty安全上下文對象)
SecurityContext securityContext = SecurityContextHolder.getContext();

// 獲取當前認證了的 principal(當事人),或者 request token (令牌)
// 如果沒有認證,會是 null,該例子是認證之后的情況
Authentication authentication = securityContext.getAuthentication()

// 獲取當事人信息對象,返回結(jié)果是 Object 類型,但實際上可以是應用程序自定義的帶有更多應用相關信息的某個類型。
// 很多情況下,該對象是 Spring Security 核心接口 UserDetails 的一個實現(xiàn)類,你可以把 UserDetails 想像
// 成我們數(shù)據(jù)庫中保存的一個用戶信息到 SecurityContextHolder 中 Spring Security 需要的用戶信息格式的
// 一個適配器。
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
	String username = ((UserDetails)principal).getUsername();
} else {
	String username = principal.toString();
}

修改SecurityContextHolder的工作模式

綜上所述,SecurityContextHolder可以工作在以下三種模式之一:

  • MODE_THREADLOCAL (缺省工作模式)
  • MODE_GLOBAL
  • MODE_INHERITABLETHREADLOCAL

???修改SecurityContextHolder的工作模式有兩種方法 :

SecurityContextHolder會自動從該系統(tǒng)屬性中嘗試獲取被設定的工作模式

程序化方式主動設置工作模式的方法

  • 設置一個系統(tǒng)屬性(system.properties) : spring.security.strategy;
  • 調(diào)用SecurityContextHolder靜態(tài)方法setStrategyName()

SecurityContextHolder源碼

本文源代碼基于 Spring Security Core 4.x.x

package org.springframework.security.core.context;

import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Constructor;

/**
 * 將一個給定的SecurityContext綁定到當前執(zhí)行線程。
 * 
 * This class provides a series of static methods that delegate to an instance of
 * org.springframework.security.core.context.SecurityContextHolderStrategy. The
 * purpose of the class is to provide a convenient way to specify the strategy that should
 * be used for a given JVM. This is a JVM-wide setting, since everything in this class is
 * static to facilitate ease of use in calling code.
 * 
 * To specify which strategy should be used, you must provide a mode setting. A mode
 * setting is one of the three valid MODE_ settings defined as
 * static final fields, or a fully qualified classname to a concrete
 * implementation of
 * org.springframework.security.core.context.SecurityContextHolderStrategy that
 * provides a public no-argument constructor.
 * 
 * There are two ways to specify the desired strategy mode String. The first
 * is to specify it via the system property keyed on #SYSTEM_PROPERTY. The second
 * is to call #setStrategyName(String) before using the class. If neither approach
 * is used, the class will default to using #MODE_THREADLOCAL, which is backwards
 * compatible, has fewer JVM incompatibilities and is appropriate on servers (whereas
 * #MODE_GLOBAL is definitely inappropriate for server use).
 *
 * @author Ben Alex
 *
 */
public class SecurityContextHolder {
	// ~ Static fields/initializers
	// =====================================================================================

	// 三種工作模式的定義,每種工作模式對應一種策略
	public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
	public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
	public static final String MODE_GLOBAL = "MODE_GLOBAL";

	// 類加載時首先嘗試從環(huán)境屬性中獲取所指定的工作模式
	public static final String SYSTEM_PROPERTY = "spring.security.strategy";	
	private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
	private static SecurityContextHolderStrategy strategy;

	// 初始化計數(shù)器,初始為0,
	// 1. 類加載過程中會被初始化一次,此值變?yōu)?
	// 2. 此后每次調(diào)用 setStrategyName 會對新的策略對象執(zhí)行一次初始化,相應的該值會增1
	private static int initializeCount = 0;

	static {
		initialize();
	}

	// ~ Methods
	// =====================================================================================

	/**
	 * Explicitly clears the context value from the current thread.
	 */
	public static void clearContext() {
		strategy.clearContext();
	}

	/**
	 * Obtain the current SecurityContext.
	 *
	 * @return the security context (never null)
	 */
	public static SecurityContext getContext() {
		return strategy.getContext();
	}

	/**
	 * Primarily for troubleshooting purposes, this method shows how many times the class
	 * has re-initialized its SecurityContextHolderStrategy.
	 *
	 * @return the count (should be one unless you've called
	 * #setStrategyName(String) to switch to an alternate strategy.
	 */
	public static int getInitializeCount() {
		return initializeCount;
	}

	private static void initialize() {
		if (!StringUtils.hasText(strategyName)) {
			// Set default, 設置缺省工作模式/策略 MODE_THREADLOCAL
			strategyName = MODE_THREADLOCAL;
		}

		if (strategyName.equals(MODE_THREADLOCAL)) {
			strategy = new ThreadLocalSecurityContextHolderStrategy();
		}
		else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
			strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
		}
		else if (strategyName.equals(MODE_GLOBAL)) {
			strategy = new GlobalSecurityContextHolderStrategy();
		}
		else {
			// Try to load a custom strategy
			try {
				Class<?> clazz = Class.forName(strategyName);
				Constructor<?> customStrategy = clazz.getConstructor();
				strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
			}
			catch (Exception ex) {
				ReflectionUtils.handleReflectionException(ex);
			}
		}

		initializeCount++;
	}

	/**
	 * Associates a new SecurityContext with the current thread of execution.
	 *
	 * @param context the new SecurityContext (may not be null)
	 */
	public static void setContext(SecurityContext context) {
		strategy.setContext(context);
	}

	/**
	 * Changes the preferred strategy. Do NOT call this method more than once for
	 * a given JVM, as it will re-initialize the strategy and adversely affect any
	 * existing threads using the old strategy.
	 *
	 * @param strategyName the fully qualified class name of the strategy that should be
	 * used.
	 */
	public static void setStrategyName(String strategyName) {
		SecurityContextHolder.strategyName = strategyName;
		initialize();
	}

	/**
	 * Allows retrieval of the context strategy. See SEC-1188.
	 *
	 * @return the configured strategy for storing the security context.
	 */
	public static SecurityContextHolderStrategy getContextHolderStrategy() {
		return strategy;
	}

	/**
	 * Delegates the creation of a new, empty context to the configured strategy.
	 */
	public static SecurityContext createEmptyContext() {
		return strategy.createEmptyContext();
	}

	public String toString() {
		return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount="
				+ initializeCount + "]";
	}
}

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Spring Boot讀取配置屬性常用方法解析

    Spring Boot讀取配置屬性常用方法解析

    這篇文章主要介紹了Spring Boot讀取配置屬性常用方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • java實用型-高并發(fā)下RestTemplate的正確使用說明

    java實用型-高并發(fā)下RestTemplate的正確使用說明

    這篇文章主要介紹了java實用型-高并發(fā)下RestTemplate的正確使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • springboot實現(xiàn)學生管理系統(tǒng)

    springboot實現(xiàn)學生管理系統(tǒng)

    這篇文章主要為大家詳細介紹了springboot實現(xiàn)學生管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Spring的跨域的幾個方案

    Spring的跨域的幾個方案

    這篇文章主要介紹了Spring的跨域的幾個方案,CrossOrigin、addCorsMappings、CorsFIlter等方案,具有一定的參考價值,需要的小伙伴可以參考一下,希望對你有所幫助
    2022-02-02
  • Java最常用的6個簡單的計算題

    Java最常用的6個簡單的計算題

    本篇文章給大家整理的在JAVA中最常用到的簡單的計算題,對此有興趣的朋友可以測試參考下。
    2018-02-02
  • Spring MVC 攔截器實現(xiàn)代碼

    Spring MVC 攔截器實現(xiàn)代碼

    本篇文章主要介紹了Spring MVC 攔截器的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • 在JDK和Eclipse下如何編寫和運行Java Applet

    在JDK和Eclipse下如何編寫和運行Java Applet

    本文主要介紹了在JDK和Eclipse的環(huán)境下如何編寫和運行Java Applet,圖文方式,適合初學者學習。
    2015-09-09
  • SpringBoot中使用Quartz管理定時任務的方法

    SpringBoot中使用Quartz管理定時任務的方法

    這篇文章主要介紹了SpringBoot中使用Quartz管理定時任務的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 詳解用JWT對SpringCloud進行認證和鑒權

    詳解用JWT對SpringCloud進行認證和鑒權

    這篇文章主要介紹了詳解用JWT對SpringCloud進行認證和鑒權,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • 在SpringBoot項目中優(yōu)雅的連接多臺Redis的操作方法

    在SpringBoot項目中優(yōu)雅的連接多臺Redis的操作方法

    本文將基于一個實際案例,詳細介紹如何在Spring Boot中優(yōu)雅地配置和使用多個Redis實例,解決常見的注入歧義問題,并提供一個通用的Redis工具類來簡化操作,本文給大家介紹的非常詳細,感興趣的朋友一起看看吧
    2025-09-09

最新評論

鄄城县| 新乐市| 宁明县| 洞口县| 泰安市| 海原县| 齐河县| 潞西市| 阿勒泰市| 佛山市| 东城区| 盐山县| 台北县| 志丹县| 柳江县| 施甸县| 重庆市| 泸定县| 奉新县| 秦皇岛市| 于田县| 新邵县| 滦南县| 陕西省| 临清市| 华阴市| 淮北市| 清镇市| 汾阳市| 东源县| 图木舒克市| 丰宁| 华亭县| 凤凰县| 北辰区| 旌德县| 聂荣县| 嘉峪关市| 丰县| 孝昌县| 锦屏县|