簡介
Spring Security:是一個提供身份驗證,授權(quán)和保護以防止常見攻擊的框架。 憑借對命令式和反應(yīng)式應(yīng)用程序的一流支持,它為Spring應(yīng)用程序的安全提供實際標(biāo)準(zhǔn)。
特性
Spring Security為身份驗證,授權(quán)和針對常見漏洞的防護提供了全面的支持。 它還集成了三方庫,以簡化其使用。
認(rèn)證
Spring Security為身份驗證提供了全面的支持。 身份驗證是我們驗證誰試圖訪問特定資源的身份的方法。 驗證用戶身份的常用方法是要求用戶輸入用戶名和密碼。 一旦執(zhí)行了身份驗證,我們就會知道身份并可以執(zhí)行授權(quán)。
認(rèn)證支持
Spring Security提供了用于驗證用戶的內(nèi)置支持。 分為兩大主體:基于Servlet和WebFlux的身份驗證。
密碼加密存儲
Spring Security的PasswordEncoder接口用于對密碼進行單向轉(zhuǎn)換,以使密碼可以安全地存儲。
基于PasswordEncoder接口的實現(xiàn)類如下:
DelegatingPasswordEncoder:即委托密碼編碼器,兼容多種不同加密方式存儲密碼。主要用于新舊數(shù)據(jù)的加密方式的兼容,做到平滑遷移,例如舊數(shù)據(jù)使用NoOpPasswordEncoder,新數(shù)據(jù)使用BCryptPasswordEncoder加密。
BCryptPasswordEncoder:基于bcrypt算法的編碼器,為了使其更能抵御密碼破解,bcrypt故意降低了速度,與其他自適應(yīng)單向功能一樣,應(yīng)將其調(diào)整為大約1秒鐘,以驗證系統(tǒng)上的密碼。BCryptPasswordEncoder的默認(rèn)實現(xiàn)使用強度10。
Argon2PasswordEncoder:基于 Argon2算法的編碼器,Argon2是“密碼哈希競賽”的獲勝者。 為了克服自定義硬件上的密碼破解問題,Argon2是一種故意慢速的算法,需要大量內(nèi)存。 與其他自適應(yīng)單向功能一樣,應(yīng)將其調(diào)整為大約1秒鐘,以驗證系統(tǒng)上的密碼。 Argon2PasswordEncoder的當(dāng)前實現(xiàn)需要BouncyCastle。
Pbkdf2PasswordEncoder:基于 PBKDF2算法的編碼器,為了克服密碼破解問題,PBKDF2是一種故意緩慢的算法。 與其他自適應(yīng)單向功能一樣,應(yīng)將其調(diào)整為大約1秒鐘,以驗證系統(tǒng)上的密碼。 當(dāng)需要FIPS認(rèn)證時,此算法是一個不錯的選擇。
SCryptPasswordEncoder:基于 scrypt算法的編碼器, 為了克服自定義硬件scrypt上的密碼破解問題,這是一種故意緩慢的算法,需要大量內(nèi)存。 與其他自適應(yīng)單向功能一樣,應(yīng)將其調(diào)整為大約1秒鐘,以驗證系統(tǒng)上的密碼。
防止漏洞利用
CSRF:Cross Site Request Forgery,即跨站請求偽造。Spring提供了兩種機制來防御CSRF攻擊:1、基于token表單驗證(表單增加_csrf字段);2、在會話Cookie上指定SameSite屬性;
-
安全的HTTP響應(yīng)頭:
- Cache Control:Spring Security的默認(rèn)禁用緩存以保護用戶的內(nèi)容,
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
; - Content Type Options:禁用內(nèi)容嗅探
X-Content-Type-Options: nosniff
防止XSS攻擊; - HTTP Strict Transport Security (HSTS):嚴(yán)格的安全傳輸HTTP,將http請求自動轉(zhuǎn)為https請求,
Strict-Transport-Security: max-age=31536000 ; includeSubDomains ; preload
; - X-Frame-Options:Spring Security默認(rèn)禁用iframe頁面,
X-Frame-Options: DENY
; - X-XSS-Protection:通常瀏覽器在檢測到XSS攻擊時應(yīng)采取的措施。 例如,過濾器可能會嘗試以最小侵入性的方式更改內(nèi)容以仍然呈現(xiàn)所有內(nèi)容。 有時,這種替換本身可能會成為XSS漏洞。 相反,最好是阻止內(nèi)容,而不要嘗試對其進行修復(fù)。 Spring Security默認(rèn)阻止內(nèi)容:
X-XSS-Protection: 1; mode=block
; - Content Security Policy (CSP):內(nèi)容安全策略是Web應(yīng)用程序可以用來緩解諸如跨站點腳本(XSS)之類的內(nèi)容注入漏洞的機制。
- Referrer Policy:
Referrer Policy: strict-origin-when-cross-origin
;
- Cache Control:Spring Security的默認(rèn)禁用緩存以保護用戶的內(nèi)容,
認(rèn)證原理分析(Servlet)
過濾器
DelegatingFilterProxy
Spring提供了一個名為DelegatingFilterProxy的Filter實現(xiàn),可以在Servlet容器的生命周期和Spring的ApplicationContext之間進行橋接。 Servlet容器允許使用自己的標(biāo)準(zhǔn)注冊Filters,但是它不知道Spring定義的Bean。 DelegatingFilterProxy可以通過標(biāo)準(zhǔn)的Servlet容器機制進行注冊,然后將所有工作委托給實現(xiàn)Filter的Spring Bean。
如上圖所示,DelegatingFilterProxy是一個標(biāo)準(zhǔn)的Servlet Filter,當(dāng)調(diào)用鏈路到DelegatingFilterProxy,DelegatingFilterProxy會找到達Spring管理的Filter,然后發(fā)起調(diào)用。
FilterChainProxy
如上圖所示,F(xiàn)ilterChainProxy是Spring Security提供的特殊過濾器,允許通過SecurityFilterChain委派許多過濾器實例。
SecurityFilterChain
如上圖所示,F(xiàn)ilterChainProxy使用SecurityFilterChain確定應(yīng)對此請求調(diào)用哪些Spring Security過濾器。
Security Filter是注冊到FilterChainProxy而不是DelegatingFilterProxy的。原因如下:
- 它為Spring Security的所有Servlet支持提供了一個起點。如果想對Spring Security的Servlet支持進行故障排除,那么在FilterChainProxy中添加調(diào)試點是一個很好的起點;
- 它清除SecurityContext以避免內(nèi)存泄漏;
- 它使用Spring Security的HttpFirewall來保護應(yīng)用程序免受某些類型的攻擊;
- 它在確定何時應(yīng)調(diào)用SecurityFilterChain時提供了更大的靈活性。 在Servlet容器中,僅根據(jù)URL調(diào)用過濾器。 但是,F(xiàn)ilterChainProxy可以利用RequestMatcher接口,根據(jù)HttpServletRequest中的任何內(nèi)容確定調(diào)用。
-
FilterChainProxy可用于確定應(yīng)使用哪個SecurityFilterChain。 這允許應(yīng)用程序的不同部分提供完全獨立的配置。
image.png
如上圖所示,F(xiàn)ilterChainProxy利用RequestMatcher接口確定調(diào)用哪個SecurityFilterChain。舉個例子:如果訪問/api/getMsg,則調(diào)用SecurityFilterChain0。
Security Filter
Spring Security提供了以下Security Filter(包含順序,通過FilterComparator配置相關(guān)順序):
- ChannelProcessingFilter
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- OpenIDAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- ConcurrentSessionFilter
- DigestAuthenticationFilter
- BearerTokenAuthenticationFilter
- BasicAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- OAuth2AuthorizationCodeGrantFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- SwitchUserFilter
異常處理
如上圖表示的是異常處理過濾器ExceptionTranslationFilter的工作原理:
- ExceptionTranslationFilter 調(diào)用 FilterChain.doFilter(request, response),拋出Security Exception;
- 如果用戶未通過身份驗證或它是AuthenticationException,則啟動身份驗證:
- 清除SecurityContextHolder;
- 將HttpServletRequest保存在RequestCache中;
- 進入AuthenticationEntryPoint(重定向到登錄頁或者返回401);
- 另外一種異常就是,訪問被拒絕,則交給AccessDeniedHandler來處理。
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, RuntimeException exception) throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
}
else if (exception instanceof AccessDeniedException) {
handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
}
}
認(rèn)證
認(rèn)證相關(guān)組件
SecurityContextHolder:用于存儲SecurityContext;
SecurityContext:用于存儲Authentication;
-
Authentication:用戶存儲用戶認(rèn)證詳細信息;image.png
該圖為SecurityContextHolder、SecurityContext、Authentication三者之間的關(guān)系。
AuthenticationManager:統(tǒng)一管理執(zhí)行身份驗證;
ProviderManager:AuthenticationManager最常見的實現(xiàn);
-
AuthenticationProvider:用于執(zhí)行特定類型的身份驗證,由ProviderManager統(tǒng)一管理。
image.png
如上圖所示,ProviderManager作為AuthenticationManager最常見的實現(xiàn),ProviderManager認(rèn)證時,將認(rèn)證邏輯委托給AuthenticationProvider列表,不同AuthenticationProvider的執(zhí)行不同的認(rèn)證邏輯。
如果沒有AuthenticationProvider可以執(zhí)行身份驗證,使用該父AuthenticationManager(通常是ProviderManager實例)進行認(rèn)證。
一個父AuthenticationManager可能存在多個ProviderManager實例。 即具有相同身份驗證(共享父AuthenticationManager)但又具有不同身份驗證機制(不同ProviderManager實例)。
AuthenticationEntryPoint:認(rèn)證入口,用戶未登錄時,重定向到登錄頁面或者發(fā)送401、WWW-Authenticate響應(yīng)頭;ExceptionTranslationFilter 拋出AuthenticationException的時候,調(diào)用sendStartAuthentication方法時進入AuthenticationEntryPoint邏輯。
-
AbstractAuthenticationProcessingFilter:作為身份驗證的基本過濾器(formLogin表單登錄)。 當(dāng)用戶提交身份憑據(jù)(例如,賬號密碼)時過濾。image.png
上圖表示用戶提交身份憑據(jù)的認(rèn)證邏輯:
- 假設(shè)用戶在登錄頁提交表單(賬號密碼)進行登錄;
- AbstractAuthenticationProcessingFilter判斷該請求是提交身份憑據(jù)認(rèn)證請求,則調(diào)用attemptAuthentication方法也就是上圖中的①;
- attemptAuthentication將認(rèn)證邏輯委托給AuthenticationManager就是上圖中的②;
- 認(rèn)證失敗會做一些清理動作就是上圖中的③;
- 認(rèn)證成功,作Session相關(guān)的邏輯、將Authentication存儲到SecurityContextHolder、RememberMe相關(guān)處理、認(rèn)證成功事件的發(fā)布、認(rèn)證成功的后置處理。
-
SessionAuthenticationStrategy:旨在身份驗證時對HttpSession相關(guān)行為進行管理,如會話固定保護,csrf防護,session并發(fā)控制等。image.png
如上圖所示,兩種登錄方式:UsernamePasswordAuthenticationFilter(基于表單)與BasicAuthenticationFilter(基于Rest)。有兩條請求鏈路:
- 客戶端請求經(jīng)過UsernamePasswordAuthenticationFilter過濾器,驗證用戶登錄憑證后,調(diào)用SessionAuthenticationStrategy.onAuthentication()基于session的驗證;
-
客戶端請求經(jīng)過BasicAuthenticationFilter過濾器,驗證用戶登錄憑證后,需要再經(jīng)過SessionManagementFilter過濾器,SessionManagementFilter接著將session驗證委托給SessionAuthenticationStrategy。
image.png
上圖SessionAuthenticationStrategy的調(diào)用邏輯:
- 首先進入CompositeSessionAuthenticationStrategy,該策略意為混合session認(rèn)證策略,它把session認(rèn)證邏輯循環(huán)委托給其他具體的策略;
- ConcurrentSessionControlAuthenticationStrategy,并發(fā)session控制,用于同一個用戶多次登錄管理,默認(rèn)行為會剔除(標(biāo)記過期SessionInformation.expireNow())最近最久沒有使用的登錄,
maximumSessions=1
配置一個賬號最多幾個登錄共存; - ChangeSessionIdAuthenticationStrategy,顧名思義變更當(dāng)前sessionId,目的為了會話固定防護,該類繼承了AbstractSessionFixationProtectionStrategy。
- RegisterSessionAuthenticationStrategy,為當(dāng)前的新的sessionId注冊一個SessionInformation(為了支持并發(fā)session控制);
- CsrfAuthenticationStrategy,支持csrf防護,生成新的token。
講到這里大家可能會有疑問,剛才提到SessionInformation是干么用的呢?
SessionInformation是為了支持session并發(fā)控制,ConcurrentSessionControlAuthenticationStrategy只是將SessionInformation標(biāo)記為過期,等下次請求的時候會經(jīng)過ConcurrentSessionFilter過濾器,此時會判斷當(dāng)前session對應(yīng)的SessionInformation是否被標(biāo)記過期了,如果是則調(diào)用session銷毀動作,真正地登出。如果不是,得刷新更新SessionInformation的lastRequest(最近一次請求時間),ConcurrentSessionControlAuthenticationStrategy是基于該時間去剔除最近最久沒有使用的登錄。
認(rèn)證機制
- Username and Password
- Remember Me
- CAS
- OAuth 2.0
- SAML 2.0
- JAAS Authentication
- OpenID
- Pre-Authentication Scenarios
- X509 Authentication
Username and Password認(rèn)證
驗證用戶身份的最常見方法之一是驗證用戶名和密碼, Spring Security為使用用戶名和密碼進行身份驗證提供了全面的支持。
認(rèn)證方式
-
Form Login:基于表單登錄認(rèn)證;
image.png
上圖表示未登錄用戶第一次訪問web系統(tǒng)Spring Security處理流程:
- 用戶在瀏覽器發(fā)起請求web系統(tǒng)私有資源
/private
; - SecurityFilterChain過濾器鏈路到達FilterSecurityInterceptor,并拋出訪問被拒絕的異常AccessDeniedException,ExceptionTranslationFilter捕獲該異常并通過sendStartAuthentication方法進入LoginUrlAuthenticationEntryPoint(AuthenticationEntryPoint的實現(xiàn)類);
- LoginUrlAuthenticationEntryPoint設(shè)置了一個重定向到
/login
頁面的響應(yīng)頭; - 瀏覽器重定向到
/login
,并獲取到login.html
,即登錄主頁面。
image.png
上圖表示用戶在表單提交用戶名、密碼的驗證流程:
-
用戶提交表單,進入UsernamePasswordAuthenticationFilter過濾器,通過調(diào)用attemptAuthentication方法將用戶名、密碼包裝成UsernamePasswordAuthenticationToken對象,并傳給AuthenticationManager;
image.png上圖表示AuthenticationManager工作流程:
- 將UsernamePasswordAuthenticationToken傳給AuthenticationManager;
- ProviderManager將認(rèn)證邏輯委托為DaoAuthenticationProvider;
- DaoAuthenticationProvider調(diào)用UserDetailsService的loadUserByUsername方法獲取UserDetails,通過PasswordEncoder比較用戶請求傳遞過來UsernamePasswordAuthenticationToken上的密碼與UserDetails存儲的密碼是否一致;
- 認(rèn)證成功,將UserDetails作為principal,UserDetails的authorities作為authorities包裝成UsernamePasswordAuthenticationToken返回。
- 認(rèn)證失敗會做一些清理動作;
- 認(rèn)證成功,作Session相關(guān)的邏輯、將Authentication存儲到SecurityContextHolder、RememberMe相關(guān)處理、認(rèn)證成功事件的發(fā)布、認(rèn)證成功的后置處理。
-
Basic Authentication:基礎(chǔ)認(rèn)證,基本HTTP REST的身份驗證。
image.png
上圖表示未登錄用戶第一次訪問web系統(tǒng)Spring Security處理流程:
- 用戶在瀏覽器發(fā)起請求web系統(tǒng)私有資源
/private
; - SecurityFilterChain過濾器鏈路到達FilterSecurityInterceptor,并拋出訪問被拒絕的異常AccessDeniedException,ExceptionTranslationFilter捕獲該異常并通過sendStartAuthentication方法進入BasicAuthenticationEntryPoint(AuthenticationEntryPoint的實現(xiàn)類);
- BasicAuthenticationEntryPoint設(shè)置了
WWW-Authenticate
響應(yīng)頭及401
響應(yīng)碼并返回。
image.png
上圖表示當(dāng)客戶端收到WWW-Authenticate響應(yīng)頭時,使用用戶名和密碼登錄的流程:
- 當(dāng)用戶提交其用戶名和密碼時,BasicAuthenticationFilter通過從HttpServletRequest中提取用戶名和密碼來創(chuàng)建UsernamePasswordAuthenticationToken;
- 將UsernamePasswordAuthenticationToken傳遞到AuthenticationManager進行身份驗證(這邊的AuthenticationManager的認(rèn)證邏輯與Form Login相同);
- 認(rèn)證失敗會做一些清理動作;
- 認(rèn)證成功,將Authentication存儲到SecurityContextHolder、RememberMe相關(guān)處理、調(diào)用FilterChain.doFilter(request,response)繼續(xù)請求邏輯。
- Digest Authentication:摘要式身份驗證;不建議在現(xiàn)代應(yīng)用程序中使用摘要式身份驗證,因為它不安全。 它必須以純文本,加密或MD5格式存儲密碼,這些存儲格式都被認(rèn)為是不安全的。不支持的單向自適應(yīng)密碼哈希(即bCrypt,PBKDF2,SCrypt等)存儲憑據(jù)。
Remember-Me認(rèn)證
Remember-Me(記住我),主要用于在一段很長的時間內(nèi)(通常15天),用戶只需要登錄一次,就無需再登錄了(前提是用戶名、密碼、秘鑰不變的情況)。
原理:當(dāng)用戶登錄成功時,服務(wù)端會向瀏覽器額外發(fā)送一個cookie(name = remember-me, value = token值),之后的請求都會攜帶這個cookie,當(dāng)用戶session失效時(比如2小時過后),該cookie攜帶到服務(wù)端觸發(fā)自動登錄。
當(dāng)然,Remember-Me會存在一些安全問題,Remember-Me的token可以被用戶代理捕獲到,可以輕松通過該token去修改密碼。因此在一些安全性重要的應(yīng)用上面,不建議開啟Remember-Me。
存儲機制
- In-Memory:內(nèi)存存儲;Spring Security的InMemoryUserDetailsManager實現(xiàn)了UserDetailsS??ervice,用來管理內(nèi)存中的用戶名/密碼。
- JDBC:數(shù)據(jù)庫存儲;使用JdbcUserDetailsManager基于JDBC的用戶名/密碼身份驗證。
- 自定義存儲:使用UserDetailsService可自定義存儲方式;例如:
// CustomUserDetailsS??ervice是UserDetailsS??ervice的實現(xiàn)類
@Bean
CustomUserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
- LDAP:LDAP存儲,有興趣可以自行查閱官方文檔;
授權(quán)原理分析(Servlet)
FilterSecurityInterceptor授權(quán)
FilterSecurityInterceptor為HttpServletRequests提供授權(quán),它作為安全篩選器之一插入到FilterChainProxy中(除此之外,Spring Security支持服務(wù)層方法授權(quán)還有域?qū)ο笫跈?quán))。
- FilterSecurityInterceptor可以從SecurityContextHolder獲取Authentication;
- FilterSecurityInterceptor將HttpServletRequest、HttpServletResponse、FilterChain包裝成FilterInvocation對象,調(diào)用父類AbstractSecurityInterceptor.beforeInvocation(),進入前置處理階段;
- AbstractSecurityInterceptor從SecurityMetadataSource獲取配置信息
ConfigAttribute(例如:hasRole('ROLE_USER')
);調(diào)用AbstractSecurityInterceptor.authenticateIfRequired()執(zhí)行認(rèn)證相關(guān)邏輯; - 調(diào)用AccessDecisionManager.decide()方法,進行訪問決策處理,該方法將決策委托給AccessDecisionVoter訪問決策投票器,進行投票處理,AccessDecisionManager將投票的結(jié)果進行整合;
- 如果AccessDecisionManager決策被拒絕,則拋出AccessDeniedException,ExceptionTranslationFilter捕獲到這個異常,并交給AuthenticationEntryPoint進行相應(yīng)的處理,例如跳轉(zhuǎn)登錄頁、返回403狀態(tài)等;
- 如果AccessDecisionManager決策成功,則方法正常調(diào)用chain.doFilter();
- 方法正常調(diào)用完成后,通過AbstractSecurityInterceptor.afterInvocation()進入后置處理階段(某些應(yīng)用程序需要修改實際返回的對象);
Authorities權(quán)限列表
回顧下前面學(xué)到的知識,用戶登錄認(rèn)證成功后,會為當(dāng)前用戶生成一個Authentication對象,該對象包含了Principal、Credentials和Authorities;
該Authorities由GrantedAuthority(默認(rèn)實現(xiàn):SimpleGrantedAuthority)集合組成,GrantedAuthority接口只有一個方法,如下:
String getAuthority();
一般情況下GrantedAuthority由String表示,如果GrantedAuthority無法精確地表示為String,則GrantedAuthority被視為“復(fù)雜”,并且getAuthority()必須返回null。
前置處理階段
剛剛講到Spring Security調(diào)用AbstractSecurityInterceptor.beforeInvocation()進入前置處理階段,該階段的一個重點就是進行訪問決策處理,由AccessDecisionManager相關(guān)實現(xiàn)來完成,AccessDecisionManager接口包含三個方法:
// 決策處理邏輯
void decide(Authentication authentication, Object secureObject,
Collection<ConfigAttribute> attrs) throws AccessDeniedException;
// 項目啟動校驗配置是否正確
boolean supports(ConfigAttribute attribute);
// 項目啟動校驗配置是否正確
boolean supports(Class clazz);
如上圖所示,Spring Security提供了三個決策處理器AccessDecisionManager的實現(xiàn)類(AffirmativeBased、ConsensusBased、UnanimousBased),代表三種不同的決策處理器,當(dāng)然也可以自定義決策處理器。決策處理器將決策邏輯委托給多個投票器AccessDecisionVoter(具體實現(xiàn)有:AuthenticatedVoter、RoleVoter、WebExpressionVoter等),接著AccessDecisionManager將投票結(jié)果進行整合,返回拒絕或者成功。
AccessDecisionManager實現(xiàn)類的具體描述如下:
- AffirmativeBased:只要有一票通過,返回成功;
- ConsensusBased:通過票數(shù) > 不通過票數(shù),返回成功;如果通過票數(shù) = 不通過票數(shù),這個時候配置
allowIfEqualGrantedDeniedDecisions=true
,即可返回成功; - UnanimousBased:與上面兩個不同的是,UnanimousBased需要輪詢每個ConfigAttribute,然后投票器對每個ConfigAttribute進行投票,只要有一票不通過,則返回失敗。
AccessDecisionVoter通過返回int來表示投票的結(jié)果,有ACCESS_ABSTAIN(0,棄權(quán)),ACCESS_DENIED(-1,不通過)和ACCESS_GRANTED(1,通過),AccessDecisionVoter主要的實現(xiàn)類如下:
- RoleVoter:角色投票器,最常用的投票器。如果任何ConfigAttribute以前綴ROLE_開頭,它將進行投票,否則投票者將棄權(quán)ACCESS_ABSTAIN,如果存在GrantedAuthority可以返回一個字符串表示形式(通過getAuthority()方法),該字符串表示形式完全等于一個或多個以前綴ROLE_開頭的ConfigAttributes,則它將投票通過ACCESS_GRANTED,否則投票不通過ACCESS_DENIED。
- AuthenticatedVoter:用來區(qū)分匿名,完全認(rèn)證和記住我的認(rèn)證用戶。 許多站點允許使用“記住我”身份驗證,但是某些資源確要求完全認(rèn)證方式(用戶登錄)才能訪問。
- WebExpressionVoter:web表達式投票器,基于Spring EL進行解析,例如:
hasRole('ROLE_USER')
。表達式根對象的基類是SecurityExpressionRoot,提供了Web和方法安全性中都可用的一些常用表達式。詳情參考:https://docs.spring.io/spring-security/site/docs/5.4.1/reference/html5/#el-access
后置處理階段
某些應(yīng)用程序需要一種修改返回的對象的方法,因此Spring Security提供了一個方便的掛鉤AfterInvocationManager,通過AfterInvocationManager來修改返回對象。
如上圖所示,AfterInvocationManager有一個具體的實現(xiàn)AfterInvocationProviderManager,它輪詢AfterInvocationProvider的列表。 每個AfterInvocationProvider都可以修改返回對象或引發(fā)AccessDeniedException。 實際上,由于前一個提供程序的結(jié)果將傳遞到列表中的下一個,因此多個提供程序可以修改該對象。