建立健全制度如何推廣seo
一、前言
此篇是對上篇 Spring Security 6.x 系列(5)—— Servlet 認證體系結(jié)構(gòu)介紹 中4.9章節(jié)顯式調(diào)用SecurityContextRepository#saveContext
進行詳解分析。
二、設(shè)置和修改登錄態(tài)
2.1 登錄態(tài)存儲形式
使用Spring Security
框架,認證成功后的用戶信息會放在Authentication
對象的Principal
中,
Authentication
對象又會被放入SecurityContext
中,而SecurityContext
存在這2個地方:
-
SecurityContextHolderStrategy
:線程級別的SecurityContext
持有策略。有全局共享、線程繼承、線程隔離等幾種獲取上下文的方式(上文有過介紹)。 -
SecurityContextRepository
:持久化SecurityContext
,默認存入HttpServletRequest
和HttpSession
。
在代碼中,我們要獲取用戶登錄信息,可以通過SecurityContextHolder.getContext().getAuthentication()
的方式獲取,這種方式是從SecurityContextHolderStrategy
獲取用戶數(shù)據(jù)。而SecurityContextHolderStrategy
初始化數(shù)據(jù)又是來自SecurityContextRepository
,相關(guān)邏輯是在SecurityContextHolderFilter
類里。
SecurityContextHolderFilter#doFilter
源碼如下:
查看securityContextRepository
如下:
設(shè)想一種場景,在用戶登錄后,編輯了用戶信息,這時要同步刷新SecurityContext
里的用戶信息。我們要如何更新這兩個處的用戶數(shù)據(jù)呢?
2.2 更新SecurityContextHolderStrategy中的用戶信息
要更新SecurityContextHolderStrategy
非常簡單,因為它保存在內(nèi)存里,只要通過SecurityContextHolder.getContext().getAuthentication()
獲取認證信息后,直接設(shè)置對應(yīng)的屬性,內(nèi)存中屬性值發(fā)生變化,后續(xù)處理邏輯就能讀到最新值。
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
MyUser myUser = (MyUser) authentication.getPrincipal();
myUser.setNickname("新的昵稱");
2.3 更新SecurityContextRepository中的用戶信息
2.3.1 了解SecurityContextRepository接口
SecurityContextRepository
是一個接口,源碼如下:
public interface SecurityContextRepository {/** @deprecated */@DeprecatedSecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);default DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {Supplier<SecurityContext> supplier = () -> {return this.loadContext(new HttpRequestResponseHolder(request, (HttpServletResponse)null));};return new SupplierDeferredSecurityContext(SingletonSupplier.of(supplier), SecurityContextHolder.getContextHolderStrategy());}void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);boolean containsContext(HttpServletRequest request);
}
SecurityContextRepository
接口有以下幾個實現(xiàn)類:
- NullSecurityContextRepository:什么都不做,也就是不會存儲,每次請求都需要重新認證
- HttpSessionSecurityContextRepository:將
SecurityContext
保存在Session
中,獲取的時候,從Session
中查詢 - RequestAttributeSecurityContextRepository:將
SecurityContext
保存在請求對象HttpServletRequest
中 - DelegatingSecurityContextRepository:委派代理存儲,內(nèi)部維護多個
SecurityContextRepository
,實現(xiàn)同時支持多種方式存儲SecurityContext
。
2.3.2 無法直接獲取SecurityContextRepository對象
要知道怎么更新SecurityContextRepository
,我們先看其他地方是怎么調(diào)用它。往SecurityContextRepository
寫數(shù)據(jù)是在用戶認證成功之后,調(diào)用AbstractAuthenticationProcessingFilter#successfulAuthentication()
方法,執(zhí)行認證成功的后續(xù)邏輯時。
這里的this.securityContextRepository
是通過setter
方法傳進來的,并且發(fā)現(xiàn)Spring Security
沒有把SecurityContextRepository
注冊到Spring
容器,而且Spring Security
其他持有SecurityContextRepository
對象的類都沒有暴露SecurityContextRepository
的獲取方法。也就是說,我們無法從Spring Security
拿到默認的SecurityContextRepository
對象。
debug
發(fā)現(xiàn)this.securityContextRepository
屬性指向了 DelegatingSecurityContextRepository
,這是委派代理存儲,代理的對象是 RequestAttributeSecurityContextRepository
和 HttpSessionSecurityContextRepository
,所以在默認的情況下,用戶登錄成功之后,在這里就把登錄用戶數(shù)據(jù)分別存入到HttpSessionSecurityContextRepository
和RequestAttributeSecurityContextRepository
中。
部分DelegatingSecurityContextRepository
源碼如下:
2.3.3 手動設(shè)置SecurityContextRepository對象
為了順利拿到SecurityContextRepository
對象,我們可以手動往Spring
容器注冊一個SecurityContextRepository
對象,然后把它塞到Spring Security
里。通過這種方式,我們能從Spring
容器拿到SecurityContextRepository
對象,然后隨時刷新SecurityContext
。
具體實現(xiàn)代碼如下:
@Bean
public SecurityContextRepository securityContextRepository() {return new DelegatingSecurityContextRepository(new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository());
}@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, SecurityContextRepository securityContextRepository) throws Exception {httpSecurity.securityContext((context) -> context.securityContextRepository(securityContextRepository()));
}
這里DelegatingSecurityContextRepository
是Spring Security
中的SecurityContextRepository
默認實現(xiàn),我們原封不動保留下來。
2.3.4 更新登錄態(tài)
在更新完SecurityContextHolderStrategy
對象之后,我們顯式把SecurityContext
重新保存到SecurityContextRepository
。
// 更新SecurityContextHolderStrategy
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
MyUser myUser = (MyUser) authentication.getPrincipal();
myUser.setNickname("新的昵稱");// 更新SecurityContextRepository
securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
三、總結(jié)
通過顯式更新SecurityContextHolderStrategy
、SecurityContextRepository
,我們就能完整更新SecurityContext
中的用戶信息。如果項目中引入了Spring Session
,Spring Session
維護的登錄態(tài)也會同步更新。