Spring Security基礎(chǔ)原理

簡介

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接口用于對密碼進(jìn)行單向轉(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)容,而不要嘗試對其進(jìn)行修復(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;

認(rèn)證原理分析(Servlet)

過濾器

DelegatingFilterProxy

Spring提供了一個名為DelegatingFilterProxy的Filter實現(xiàn),可以在Servlet容器的生命周期和Spring的ApplicationContext之間進(jìn)行橋接。 Servlet容器允許使用自己的標(biāo)準(zhǔn)注冊Filters,但是它不知道Spring定義的Bean。 DelegatingFilterProxy可以通過標(biāo)準(zhǔn)的Servlet容器機制進(jìn)行注冊,然后將所有工作委托給實現(xiàn)Filter的Spring Bean。


image.png

如上圖所示,DelegatingFilterProxy是一個標(biāo)準(zhǔn)的Servlet Filter,當(dāng)調(diào)用鏈路到DelegatingFilterProxy,DelegatingFilterProxy會找到達(dá)Spring管理的Filter,然后發(fā)起調(diào)用。

FilterChainProxy

image.png

如上圖所示,F(xiàn)ilterChainProxy是Spring Security提供的特殊過濾器,允許通過SecurityFilterChain委派許多過濾器實例。

SecurityFilterChain

image.png

如上圖所示,F(xiàn)ilterChainProxy使用SecurityFilterChain確定應(yīng)對此請求調(diào)用哪些Spring Security過濾器。

Security Filter是注冊到FilterChainProxy而不是DelegatingFilterProxy的。原因如下:

  • 它為Spring Security的所有Servlet支持提供了一個起點。如果想對Spring Security的Servlet支持進(jìn)行故障排除,那么在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

異常處理

image.png

如上圖表示的是異常處理過濾器ExceptionTranslationFilter的工作原理:

  • ExceptionTranslationFilter 調(diào)用 FilterChain.doFilter(request, response),拋出Security Exception;
  • 如果用戶未通過身份驗證或它是AuthenticationException,則啟動身份驗證:
    • 清除SecurityContextHolder;
    • 將HttpServletRequest保存在RequestCache中;
    • 進(jìn)入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)證詳細(xì)信息;
    image.png

    該圖為SecurityContextHolder、SecurityContext、Authentication三者之間的關(guān)系。

  • AuthenticationManager:統(tǒng)一管理執(zhí)行身份驗證;

  • ProviderManager:AuthenticationManager最常見的實現(xiàn);

  • AuthenticationProvider:用于執(zhí)行特定類型的身份驗證,由ProviderManager統(tǒng)一管理。


    image.png
image.png

如上圖所示,ProviderManager作為AuthenticationManager最常見的實現(xiàn),ProviderManager認(rèn)證時,將認(rèn)證邏輯委托給AuthenticationProvider列表,不同AuthenticationProvider的執(zhí)行不同的認(rèn)證邏輯。

如果沒有AuthenticationProvider可以執(zhí)行身份驗證,使用該父AuthenticationManager(通常是ProviderManager實例)進(jìn)行認(rèn)證。


image.png

一個父AuthenticationManager可能存在多個ProviderManager實例。 即具有相同身份驗證(共享父AuthenticationManager)但又具有不同身份驗證機制(不同ProviderManager實例)。

  • AuthenticationEntryPoint:認(rèn)證入口,用戶未登錄時,重定向到登錄頁面或者發(fā)送401、WWW-Authenticate響應(yīng)頭;ExceptionTranslationFilter 拋出AuthenticationException的時候,調(diào)用sendStartAuthentication方法時進(jìn)入AuthenticationEntryPoint邏輯。

  • AbstractAuthenticationProcessingFilter:作為身份驗證的基本過濾器(formLogin表單登錄)。 當(dāng)用戶提交身份憑據(jù)(例如,賬號密碼)時過濾。
    image.png

    上圖表示用戶提交身份憑據(jù)的認(rèn)證邏輯:

  1. 假設(shè)用戶在登錄頁提交表單(賬號密碼)進(jìn)行登錄;
  2. AbstractAuthenticationProcessingFilter判斷該請求是提交身份憑據(jù)認(rèn)證請求,則調(diào)用attemptAuthentication方法也就是上圖中的①;
  3. attemptAuthentication將認(rèn)證邏輯委托給AuthenticationManager就是上圖中的②;
  4. 認(rèn)證失敗會做一些清理動作就是上圖中的③;
  5. 認(rèn)證成功,作Session相關(guān)的邏輯、將Authentication存儲到SecurityContextHolder、RememberMe相關(guān)處理、認(rèn)證成功事件的發(fā)布、認(rèn)證成功的后置處理。
  • SessionAuthenticationStrategy:旨在身份驗證時對HttpSession相關(guān)行為進(jì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)用邏輯:

    • 首先進(jìn)入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為使用用戶名和密碼進(jìn)行身份驗證提供了全面的支持。

認(rèn)證方式
  • Form Login:基于表單登錄認(rèn)證;


    image.png

    上圖表示未登錄用戶第一次訪問web系統(tǒng)Spring Security處理流程:

  1. 用戶在瀏覽器發(fā)起請求web系統(tǒng)私有資源 /private
  2. SecurityFilterChain過濾器鏈路到達(dá)FilterSecurityInterceptor,并拋出訪問被拒絕的異常AccessDeniedException,ExceptionTranslationFilter捕獲該異常并通過sendStartAuthentication方法進(jìn)入LoginUrlAuthenticationEntryPoint(AuthenticationEntryPoint的實現(xiàn)類);
  3. LoginUrlAuthenticationEntryPoint設(shè)置了一個重定向到/login頁面的響應(yīng)頭;
  4. 瀏覽器重定向到/login,并獲取到login.html,即登錄主頁面。
    image.png

上圖表示用戶在表單提交用戶名、密碼的驗證流程:

  1. 用戶提交表單,進(jìn)入UsernamePasswordAuthenticationFilter過濾器,通過調(diào)用attemptAuthentication方法將用戶名、密碼包裝成UsernamePasswordAuthenticationToken對象,并傳給AuthenticationManager;


    image.png

    上圖表示AuthenticationManager工作流程:

    1. 將UsernamePasswordAuthenticationToken傳給AuthenticationManager;
    2. ProviderManager將認(rèn)證邏輯委托為DaoAuthenticationProvider;
    3. DaoAuthenticationProvider調(diào)用UserDetailsService的loadUserByUsername方法獲取UserDetails,通過PasswordEncoder比較用戶請求傳遞過來UsernamePasswordAuthenticationToken上的密碼與UserDetails存儲的密碼是否一致;
    4. 認(rèn)證成功,將UserDetails作為principal,UserDetails的authorities作為authorities包裝成UsernamePasswordAuthenticationToken返回。
  1. 認(rèn)證失敗會做一些清理動作;
  2. 認(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處理流程:

  1. 用戶在瀏覽器發(fā)起請求web系統(tǒng)私有資源 /private
  2. SecurityFilterChain過濾器鏈路到達(dá)FilterSecurityInterceptor,并拋出訪問被拒絕的異常AccessDeniedException,ExceptionTranslationFilter捕獲該異常并通過sendStartAuthentication方法進(jìn)入BasicAuthenticationEntryPoint(AuthenticationEntryPoint的實現(xiàn)類);
  3. BasicAuthenticationEntryPoint設(shè)置了WWW-Authenticate響應(yīng)頭及401響應(yīng)碼并返回。
    image.png

上圖表示當(dāng)客戶端收到WWW-Authenticate響應(yīng)頭時,使用用戶名和密碼登錄的流程:

  1. 當(dāng)用戶提交其用戶名和密碼時,BasicAuthenticationFilter通過從HttpServletRequest中提取用戶名和密碼來創(chuàng)建UsernamePasswordAuthenticationToken;
  2. 將UsernamePasswordAuthenticationToken傳遞到AuthenticationManager進(jìn)行身份驗證(這邊的AuthenticationManager的認(rèn)證邏輯與Form Login相同);
  3. 認(rèn)證失敗會做一些清理動作;
  4. 認(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))。


image.png
  1. FilterSecurityInterceptor可以從SecurityContextHolder獲取Authentication;
  2. FilterSecurityInterceptor將HttpServletRequest、HttpServletResponse、FilterChain包裝成FilterInvocation對象,調(diào)用父類AbstractSecurityInterceptor.beforeInvocation(),進(jìn)入前置處理階段;
  3. AbstractSecurityInterceptor從SecurityMetadataSource獲取配置信息
    ConfigAttribute(例如:hasRole('ROLE_USER'));調(diào)用AbstractSecurityInterceptor.authenticateIfRequired()執(zhí)行認(rèn)證相關(guān)邏輯;
  4. 調(diào)用AccessDecisionManager.decide()方法,進(jìn)行訪問決策處理,該方法將決策委托給AccessDecisionVoter訪問決策投票器,進(jìn)行投票處理,AccessDecisionManager將投票的結(jié)果進(jìn)行整合;
  5. 如果AccessDecisionManager決策被拒絕,則拋出AccessDeniedException,ExceptionTranslationFilter捕獲到這個異常,并交給AuthenticationEntryPoint進(jìn)行相應(yīng)的處理,例如跳轉(zhuǎn)登錄頁、返回403狀態(tài)等;
  6. 如果AccessDecisionManager決策成功,則方法正常調(diào)用chain.doFilter();
  7. 方法正常調(diào)用完成后,通過AbstractSecurityInterceptor.afterInvocation()進(jìn)入后置處理階段(某些應(yīng)用程序需要修改實際返回的對象);

Authorities權(quán)限列表

image.png

回顧下前面學(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()進(jìn)入前置處理階段,該階段的一個重點就是進(jìn)行訪問決策處理,由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);
image.png

如上圖所示,Spring Security提供了三個決策處理器AccessDecisionManager的實現(xiàn)類(AffirmativeBased、ConsensusBased、UnanimousBased),代表三種不同的決策處理器,當(dāng)然也可以自定義決策處理器。決策處理器將決策邏輯委托給多個投票器AccessDecisionVoter(具體實現(xiàn)有:AuthenticatedVoter、RoleVoter、WebExpressionVoter等),接著AccessDecisionManager將投票結(jié)果進(jìn)行整合,返回拒絕或者成功。

AccessDecisionManager實現(xiàn)類的具體描述如下:

  • AffirmativeBased:只要有一票通過,返回成功;
  • ConsensusBased:通過票數(shù) > 不通過票數(shù),返回成功;如果通過票數(shù) = 不通過票數(shù),這個時候配置allowIfEqualGrantedDeniedDecisions=true,即可返回成功;
  • UnanimousBased:與上面兩個不同的是,UnanimousBased需要輪詢每個ConfigAttribute,然后投票器對每個ConfigAttribute進(jìn)行投票,只要有一票不通過,則返回失敗。

AccessDecisionVoter通過返回int來表示投票的結(jié)果,有ACCESS_ABSTAIN(0,棄權(quán)),ACCESS_DENIED(-1,不通過)和ACCESS_GRANTED(1,通過),AccessDecisionVoter主要的實現(xiàn)類如下:

  • RoleVoter:角色投票器,最常用的投票器。如果任何ConfigAttribute以前綴ROLE_開頭,它將進(jìn)行投票,否則投票者將棄權(quán)ACCESS_ABSTAIN,如果存在GrantedAuthority可以返回一個字符串表示形式(通過getAuthority()方法),該字符串表示形式完全等于一個或多個以前綴ROLE_開頭的ConfigAttributes,則它將投票通過ACCESS_GRANTED,否則投票不通過ACCESS_DENIED。
  • AuthenticatedVoter:用來區(qū)分匿名,完全認(rèn)證和記住我的認(rèn)證用戶。 許多站點允許使用“記住我”身份驗證,但是某些資源確要求完全認(rèn)證方式(用戶登錄)才能訪問。
  • WebExpressionVoter:web表達(dá)式投票器,基于Spring EL進(jìn)行解析,例如:hasRole('ROLE_USER')。表達(dá)式根對象的基類是SecurityExpressionRoot,提供了Web和方法安全性中都可用的一些常用表達(dá)式。詳情參考:https://docs.spring.io/spring-security/site/docs/5.4.1/reference/html5/#el-access

后置處理階段

某些應(yīng)用程序需要一種修改返回的對象的方法,因此Spring Security提供了一個方便的掛鉤AfterInvocationManager,通過AfterInvocationManager來修改返回對象。


image.png

如上圖所示,AfterInvocationManager有一個具體的實現(xiàn)AfterInvocationProviderManager,它輪詢AfterInvocationProvider的列表。 每個AfterInvocationProvider都可以修改返回對象或引發(fā)AccessDeniedException。 實際上,由于前一個提供程序的結(jié)果將傳遞到列表中的下一個,因此多個提供程序可以修改該對象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容