原文連接: https://blog.gaoyuexiang.cn/2020/06/13/spring-security-authorization/,
內容無差異。
在前面的文章裏,咱們對 Spring Security
進行權限驗證的組件有了大體的瞭解,咱們首先來回顧並探究一下細節。java
這是 AbstractSecurityInterceptor
的一個子類,而且實現了 Filter
接口,負責調用父類的 beforeInvocation()
、afterInvocatio()
和finallyInvocation()
方法以及一些 Servlet 相關的工做。
真正處理權限驗證的代碼,其實在父類中。 它存在的意義就是爲了能在 Filter
中進行權限驗證。spring
這個 Filter
默認老是被安排在 SecurityFilterChain
的最後,由於須要保證它在全部的身份認證相關的 Filter
以後。mvc
這個類實現了真正的權限驗證的邏輯,它有多個子類,是爲了適配不一樣的技術而存在的,好比上面的FilterSecurityInterceptor
就是爲了適配 Servlet Filter 而存在的。ide
咱們能夠關注一下上面提到的三個方法,這是每一個子類都會調用的。ui
子類的實現老是下面的套路:spa
InterceptorStatusToken token = super.beforeInvocation(secureObject); // 1 try { // call target method, eg, filterChain.doFilter() // may get a returnedObject } final { super.finallyInvocation(token); } super.afterInvocation(token, returnedObject);
secureObject
是一個方法調用,它的類型是 Object
,但通常會看到MethodInvocation
或者 FilterInvocation
這樣的類型。這個方法的目標是調用 AccessDecisionManager.decide()
方法,完成
pre-invocation handling 操做。代理
在前面的link:/2020/05/31/spring-security-servlet-overview#_權限驗證[概覽]中介紹過,AccessDecisionManager.decide()
方法有三個參數。其中的 secureObject 已經被子類傳進來了。
那麼在真正調用前,就會去獲取 Authentication
對象和Collection<ConfigAttribute>
集合,而後進行 pre-invocation handling
操做。code
後面會介紹
ConfigAttribute
若是調用時出現 AccessDecisionException
,那麼他將會被ExceptionTranslationFilter
處理。對象
在經過權限驗證以後,就會準備一個 InterceptorStatusToken
對象做爲返回值。blog
在建立 token 以前,會嘗試使用 RunAsManager
建立一個 Authentication
對象,若是這個對象不爲 null
,那麼就會把它放入一個SecurityContext
,替換掉 SecurityContextHolder
中原有的那個。
原有的 SecurityContext
老是會被放到 token 中。
關於RunAsManager
:這裏的邏輯是替換掉SecurityContextHolder
中的值,這樣在目標方法中看到的Authentication
對象就是這個RunAsManager
建立的對象。在目標方法調用完成後,即 link:#_finallyinfocation_方法[finallyInvocation 方法] 中,會將原來的SecurityContext
從新放回SecurityContextHolder
中。這樣的目的是爲了將認證與鑑權流程中的
Authentication
對象與業務方法中的區分開來。
在上面的這些步驟中,還會發出一些 ApplicationEvent
,包括:PublicInvocationEvent
、AuthorizationFailureEvent
和AuthorizedEvent
。
PublicInvocationEvent
只在Collection<ConfigAttribute>
爲空的時候纔會發生,並且這種時候不會調用AccessDecisionManager
。
afterInvocation
方法主要目的是爲了根據 returnedObject
進行權限驗證,這使用到了 AfterInvocationManager
這個接口,這是在概覽裏沒有提到的,它被用來進行
after invocation handling。
在這個方法中,若是有必要的話,就會使用 AfterInvocationManager.decide()
方法來處理 returnedObject
,獲得一個新的結果做爲 returntedObject
。
這裏的有必要是指:
- token != null
afterInvocationManager
字段不爲空
這個方法接收 InterceptorStatusToken
做爲參數,只作一件事情:將 token
中的 SecurityContext
對象放回 SecurityContextHolder
中。
這個操做有兩個判斷條件:
contextHolderRefreshRequired
爲 true
。當SecurityContextHolder
中的值在 beforeInvocation
true
權限驗證的入口 FilterSecurityInterceptor
的介紹就到這裏,接下來咱們來看看 pre-invocation handling 和 after
invocation handling 的內容,也就是 AccessDecisionManager
與AfterInvocationManager
。
這是在概覽中介紹過的內容,這裏能夠快速的回顧一下。
AccessDecisionManager
是 pre-invocation handling 的入口。
它的三個具體實現會調用多個 AccessDecisionVoter
的實現,而後具體實現的策略來決定如何根據 voter
的結果來判斷是否經過身份驗證。 每個 voter 都會根據當前的Authentication
對象、secureObject
和 Collection<ConfigAttribute>
來作出是否容許訪問的選擇。
AccessDecisionManager
的三個實現,其實就是三種根據 voter
結果來決定最終結果的策略,分別是 AffirmativeBased
、ConsensusBased
和UnanimousBased
。策略顧名思義,就不解釋了。
以前沒有講 after invocation handling
的部分,是以爲不重要,使用場景很少(實際上是本身沒遇到)。如今想講一講,是由於發現spring-security-acl
使用到了 after invocation handling
的機制。那麼咱們就來看看 AfterInvocationManager
是怎麼工做的。
acl
的部分涉及一些新的概念,準備單獨寫一篇。
經過這個圖,咱們能夠清楚的瞭解到,AfterInvocationManager
也只是一個接口。 它的實現 AfterInvocationProviderManager
則是管理了「不少的」 AfterInvocationProvider
來真正的執行權限驗證的操做。
這裏「不少的」AfterInvocationProvider
其實也就只有四個個實現,其中三個都是acl
,包括圖裏的這兩個。
剩下的那個 PostInvocationAdviceProvider
其實也沒有真正進行
authorization 操做,而是代理給了 PostInvocationAuthorizationAdvice
處理。 而這個 PostInvocationAuthorizationAdvice
也只有ExpressionBasedPostInvocationAdvice
這一個實現,也就是基於 SpEL
表達式來進行 authorization 的實現。
而上面提到的全部的 manager 和 provider,都提供了 decide
方法用來作權限驗證。 與 AccessDecisionManager.decide()
相比,這些方法多了一個 returnedObject
參數。
這既是由於它須要做爲判斷條件參與到決策過程當中,也是由於它可能會在決策過程當中被處理,而後返回一個新的returnedObject
做爲處理後的結果。
這個類是用來存儲咱們的 Security 的配置的。
舉個例子,下面的代碼就會生成相應的 ConfigAttribute
:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .mvcMatchers("hello") .hasAuthority("test") .anyRequest() .authenticated(); }
上面的代碼定義了:
/hello
的請求須要具備 test
權限這樣咱們就能獲得這樣的 ConfigAttribute
:
這是 FilterSecurityInterceptor
的截圖。 其中的securityMetadataSource
存儲了不少的 ConfigAttribute
。AbstractSecurityInterceptor
經過子類實現的obtainSecurityMetadataSource
方法獲取到它,而後經過它獲取到本次使用的Collection<ConfigAttribute>
。
截圖中的 requestMap
保存了 RequestMatcher
與Collection<ConfigAttribute>
的關係。
當咱們請求 /hello
時,就會獲得第一個Collection<ConfigAttribute>
,也就是包含了 hasAuthority('test')
的那一個。 當咱們請求其餘接口時,就會獲得第二個。
接着,這些被獲取到的 ConfigAttribute
就能夠被後續的驗證邏輯使用到。
本文介紹了 Spring Security Authorization,並着重介紹了FilterSecurityInterceptor
如何在 SecurityFilterChain
的最後使用AccessDecisionManager
和 AfterInvocationManager
來實現
pre-invocation handling 和 after invocation handling。
對於 AccessDecisionManager
和AfterInfocationManager
,則沒有詳細介紹內部的邏輯,而是介紹了它們如何利用子類和其餘接口來完成權限驗證的。其內部具體的細節邏輯,讀者能夠本身研究。