Spring Security獲取用戶認(rèn)證信息的實(shí)現(xiàn)流程
登錄用戶數(shù)據(jù)獲取
SecurityContextHolder
? Spring Security 會(huì)將登錄用戶數(shù)據(jù)保存在 Session 中。但是,為了使用方便,Spring Security在此基礎(chǔ)上還做了一些改進(jìn),其中最主要的一個(gè)變化就是線程綁定。當(dāng)用戶登錄成功后,Spring Security 會(huì)將登錄成功的用戶信息保存到 SecurityContextHolder 中。
? SecurityContextHolder 中的數(shù)據(jù)保存默認(rèn)是通過(guò)ThreadLocal 來(lái)實(shí)現(xiàn)的,使用 ThreadLocal 創(chuàng)建的變量只能被當(dāng)前線程訪問(wèn),不能被其他線程訪問(wèn)和修改,也就是用戶數(shù)據(jù)和請(qǐng)求線程綁定在一起。當(dāng)?shù)卿浾?qǐng)求處理完畢后,Spring Security 會(huì)將 SecurityContextHolder 中的數(shù)據(jù)拿出來(lái)保存到 Session 中,同時(shí)將 SecurityContexHolder 中的數(shù)據(jù)清空。以后每當(dāng)有請(qǐng)求到來(lái)時(shí),Spring Security 就會(huì)先從 Session 中取出用戶登錄數(shù)據(jù),保存到SecurityContextHolder 中,方便在該請(qǐng)求的后續(xù)處理過(guò)程中使用,同時(shí)在請(qǐng)求結(jié)束時(shí)將 SecurityContextHolder 中的數(shù)據(jù)拿出來(lái)保存到 Session 中,然后將SecurityContextHolder 中的數(shù)據(jù)清空。
? 實(shí)際上 SecurityContextHolder 中存儲(chǔ)是 SecurityContext,在 SecurityContext 中存儲(chǔ)是 Authentication。
這種設(shè)計(jì)是典型的策略設(shè)計(jì)模式:
public class SecurityContextHolder { public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL"; public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL"; public static final String MODE_GLOBAL = "MODE_GLOBAL"; private static final String MODE_PRE_INITIALIZED = "MODE_PRE_INITIALIZED"; private static SecurityContextHolderStrategy strategy; //.... private static void initializeStrategy() { if (MODE_PRE_INITIALIZED.equals(strategyName)) { Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED + ", setContextHolderStrategy must be called with the fully constructed strategy"); return; } if (!StringUtils.hasText(strategyName)) { // Set default strategyName = MODE_THREADLOCAL; } if (strategyName.equals(MODE_THREADLOCAL)) { strategy = new ThreadLocalSecurityContextHolderStrategy(); return; } if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) { strategy = new InheritableThreadLocalSecurityContextHolderStrategy(); return; } if (strategyName.equals(MODE_GLOBAL)) { strategy = new GlobalSecurityContextHolderStrategy(); return; } //..... } }
MODE THREADLOCAL
:這種存放策略是將 SecurityContext 存放在 ThreadLocal中,大家知道 Threadlocal 的特點(diǎn)是在哪個(gè)線程中存儲(chǔ)就要在哪個(gè)線程中讀取,這其實(shí)非常適合 web 應(yīng)用,因?yàn)樵谀J(rèn)情況下,一個(gè)請(qǐng)求無(wú)論經(jīng)過(guò)多少 Filter 到達(dá) Servlet,都是由一個(gè)線程來(lái)處理的。這也是 SecurityContextHolder 的默認(rèn)存儲(chǔ)策略,這種存儲(chǔ)策略意味著如果在具體的業(yè)務(wù)處理代碼中,開啟了子線程,在子線程中去獲取登錄用戶數(shù)據(jù),就會(huì)獲取不到。MODE INHERITABLETHREADLOCAL
:這種存儲(chǔ)模式適用于多線程環(huán)境,如果希望在子線程中也能夠獲取到登錄用戶數(shù)據(jù),那么可以使用這種存儲(chǔ)模式。MODE GLOBAL
:這種存儲(chǔ)模式實(shí)際上是將數(shù)據(jù)保存在一個(gè)靜態(tài)變量中,在 JavaWeb開發(fā)中,這種模式很少使用到。
SecurityContextHolderStrategy
通過(guò) SecurityContextHolder 可以得知,SecurityContextHolderStrategy 接口用來(lái)定義存儲(chǔ)策略方法
public interface SecurityContextHolderStrategy { void clearContext(); SecurityContext getContext(); void setContext(SecurityContext context); SecurityContext createEmptyContext(); }
接口中一共定義了四個(gè)方法:
clearContext
:該方法用來(lái)清除存儲(chǔ)的 SecurityContext對(duì)象。getContext
:該方法用來(lái)獲取存儲(chǔ)的 SecurityContext 對(duì)象。setContext
:該方法用來(lái)設(shè)置存儲(chǔ)的 SecurityContext 對(duì)象。create Empty Context
:該方法則用來(lái)創(chuàng)建一個(gè)空的 SecurityContext 對(duì)象。
代碼中獲取認(rèn)證之后用戶數(shù)據(jù)
@RestController public class HelloController { @RequestMapping("/hello") public String hello() { Authentication authentication = SecurityContextHolder .getContext().getAuthentication(); User principal = (User) authentication.getPrincipal(); System.out.println("身份 :"+principal.getUsername()); System.out.println("憑證 :"+authentication.getCredentials()); System.out.println("權(quán)限 :"+authentication.getAuthorities()); return "hello security"; } }
多線程情況下獲取用戶數(shù)據(jù)
@RestController public class HelloController { @RequestMapping("/hello") public String hello() { new Thread(()->{ Authentication authentication = SecurityContextHolder .getContext().getAuthentication(); User principal = (User) authentication.getPrincipal(); System.out.println("身份 :"+principal.getUsername()); System.out.println("憑證 :"+authentication.getCredentials()); System.out.println("權(quán)限 :"+authentication.getAuthorities()); }).start(); return "hello security"; } }
可以看到默認(rèn)策略,是無(wú)法在子線程中獲取用戶信息,如果需要在子線程中獲取必須使用第二種策略,默認(rèn)策略是通過(guò) System.getProperty 加載的,因此我們可以通過(guò)增加 VM Options 參數(shù)進(jìn)行修改。
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
頁(yè)面上獲取用戶信息
引入依賴
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency>
頁(yè)面加入命名空間
<html lang="en" xmlns:th="https://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
頁(yè)面中使用
<!--獲取認(rèn)證用戶名--> <ul> <li sec:authentication="principal.username"></li> <li sec:authentication="principal.authorities"></li> <li sec:authentication="principal.accountNonExpired"></li> <li sec:authentication="principal.accountNonLocked"></li> <li sec:authentication="principal.credentialsNonExpired"></li> </ul>
到此這篇關(guān)于Spring Security獲取用戶認(rèn)證信息的實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)Spring Security獲取認(rèn)證信息內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Security整合KeyCloak保護(hù)Rest API實(shí)現(xiàn)詳解
- Spring?Security中如何獲取AuthenticationManager對(duì)象
- SpringSecurity報(bào)錯(cuò)authenticationManager must be spec的解決
- Spring?Security?登錄時(shí)添加圖形驗(yàn)證碼實(shí)現(xiàn)實(shí)例
- SpringBoot?Security權(quán)限控制自定義failureHandler實(shí)例
- SpringBoot整合Security權(quán)限控制登錄首頁(yè)
- SpringBoot?整合Security權(quán)限控制的初步配置
- SpringBoot?Security使用MySQL實(shí)現(xiàn)驗(yàn)證與權(quán)限管理
相關(guān)文章
java實(shí)現(xiàn)裝飾器模式(Decorator Pattern)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)裝飾器模式Decorator Pattern,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10一個(gè)MIDP俄羅斯方塊游戲的設(shè)計(jì)和實(shí)現(xiàn)
一個(gè)MIDP俄羅斯方塊游戲的設(shè)計(jì)和實(shí)現(xiàn)...2006-12-12Java C++題解leetcode816模糊坐標(biāo)示例
這篇文章主要為大家介紹了Java C++題解leetcode816模糊坐標(biāo)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01java實(shí)現(xiàn)字符串匹配求兩個(gè)字符串的最大公共子串
這篇文章主要介紹了java實(shí)現(xiàn)求兩個(gè)字符串最大公共子串的方法,詳細(xì)的描述了兩個(gè)字符串的最大公共子串算法的實(shí)現(xiàn),需要的朋友可以參考下2016-10-10