原文鏈接:https://blog.gaoyuexiang.cn/2020/06/13/spring-security-authorization/,
內(nèi)容無(wú)差別。
在前面的文章里幔欧,我們對(duì) Spring Security
進(jìn)行權(quán)限驗(yàn)證的組件有了大致的了解,我們首先來(lái)回顧并探究一下細(xì)節(jié)鹃答。
FilterSecurityInterceptor
這是 AbstractSecurityInterceptor
的一個(gè)子類,并且實(shí)現(xiàn)了 Filter
接口突硝,負(fù)責(zé)調(diào)用父類的 beforeInvocation()
测摔、afterInvocatio()
和
finallyInvocation()
方法以及一些 Servlet 相關(guān)的工作。
真正處理權(quán)限驗(yàn)證的代碼狞换,其實(shí)在父類中避咆。 它存在的意義就是為了能在 Filter
中進(jìn)行權(quán)限驗(yàn)證舟肉。
這個(gè) Filter
默認(rèn)總是被安排在 SecurityFilterChain
的最后,因?yàn)樾枰WC它在所有的身份認(rèn)證相關(guān)的 Filter
之后查库。
AbstractSecurityInterceptor
這個(gè)類實(shí)現(xiàn)了真正的權(quán)限驗(yàn)證的邏輯路媚,它有多個(gè)子類,是為了適配不同的技術(shù)而存在的樊销,比如上面的
FilterSecurityInterceptor
就是為了適配 Servlet Filter 而存在的整慎。
我們可以關(guān)注一下上面提到的三個(gè)方法,這是每個(gè)子類都會(huì)調(diào)用的围苫。
子類的實(shí)現(xiàn)總是下面的套路:
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
是一個(gè)方法調(diào)用裤园,它的類型是Object
,但一般會(huì)看到
MethodInvocation
或者FilterInvocation
這樣的類型剂府。
beforeInfocation 方法
這個(gè)方法的目標(biāo)是調(diào)用 AccessDecisionManager.decide()
方法拧揽,完成
pre-invocation handling 操作。
在前面的概覽中介紹過(guò)腺占,AccessDecisionManager.decide()
方法有三個(gè)參數(shù)淤袜。其中的 secureObject 已經(jīng)被子類傳進(jìn)來(lái)了。
那么在真正調(diào)用前衰伯,就會(huì)去獲取 Authentication
對(duì)象和
Collection<ConfigAttribute>
集合铡羡,然后進(jìn)行 pre-invocation handling
操作。
后面會(huì)介紹
ConfigAttribute
如果調(diào)用時(shí)出現(xiàn) AccessDecisionException
意鲸,那么他將會(huì)被
ExceptionTranslationFilter
處理烦周。
在通過(guò)權(quán)限驗(yàn)證之后,就會(huì)準(zhǔn)備一個(gè) InterceptorStatusToken
對(duì)象作為返回值怎顾。
在創(chuàng)建 token 之前读慎,會(huì)嘗試使用 RunAsManager
創(chuàng)建一個(gè) Authentication
對(duì)象,如果這個(gè)對(duì)象不為 null
槐雾,那么就會(huì)把它放入一個(gè)
SecurityContext
贪壳,替換掉 SecurityContextHolder
中原有的那個(gè)。
原有的 SecurityContext
總是會(huì)被放到 token 中蚜退。
關(guān)于
RunAsManager
:這里的邏輯是替換掉SecurityContextHolder
中的值,這樣在目標(biāo)方法中看到的Authentication
對(duì)象就是這個(gè)RunAsManager
創(chuàng)建的對(duì)象彪笼。在目標(biāo)方法調(diào)用完成后钻注,即 finallyInvocation 方法 中,會(huì)將原來(lái)的SecurityContext
重新放回SecurityContextHolder
中配猫。這樣的目的是為了將認(rèn)證與鑒權(quán)流程中的
Authentication
對(duì)象與業(yè)務(wù)方法中的區(qū)分開(kāi)來(lái)幅恋。
在上面的這些步驟中,還會(huì)發(fā)出一些 ApplicationEvent
泵肄,包括:
PublicInvocationEvent
捆交、AuthorizationFailureEvent
和
AuthorizedEvent
淑翼。
PublicInvocationEvent
只在Collection<ConfigAttribute>
為空的時(shí)候才會(huì)發(fā)生,而且這種時(shí)候不會(huì)調(diào)用AccessDecisionManager
品追。
afterInfocation 方法
afterInvocation
方法主要目的是為了根據(jù) returnedObject
進(jìn)行權(quán)限驗(yàn)證玄括,這使用到了 AfterInvocationManager
這個(gè)接口,這是在概覽里沒(méi)有提到的肉瓦,它被用來(lái)進(jìn)行
after invocation handling遭京。
在這個(gè)方法中,如果有必要的話泞莉,就會(huì)使用 AfterInvocationManager.decide()
方法來(lái)處理 returnedObject
哪雕,得到一個(gè)新的結(jié)果作為 returntedObject
。
這里的有必要是指:
- token != null
afterInvocationManager
字段不為空
finallyInfocation 方法
這個(gè)方法接收 InterceptorStatusToken
作為參數(shù)鲫趁,只做一件事情:將 token
中的 SecurityContext
對(duì)象放回 SecurityContextHolder
中斯嚎。
這個(gè)操作有兩個(gè)判斷條件:
token 不為 null
token 的
contextHolderRefreshRequired
為true
。當(dāng)
SecurityContextHolder
中的值在beforeInvocation
中被替換時(shí)挨厚,這個(gè)值才為true
權(quán)限驗(yàn)證的入口 FilterSecurityInterceptor
的介紹就到這里堡僻,接下來(lái)我們來(lái)看看 pre-invocation handling 和 after
invocation handling 的內(nèi)容,也就是 AccessDecisionManager
與
AfterInvocationManager
幽崩。
AccessDecisionManager
這是在概覽中介紹過(guò)的內(nèi)容苦始,這里可以快速的回顧一下。
AccessDecisionManager
是 pre-invocation handling 的入口慌申。
它的三個(gè)具體實(shí)現(xiàn)會(huì)調(diào)用多個(gè) AccessDecisionVoter
的實(shí)現(xiàn)陌选,然后具體實(shí)現(xiàn)的策略來(lái)決定如何根據(jù) voter
的結(jié)果來(lái)判斷是否通過(guò)身份驗(yàn)證。 每一個(gè) voter 都會(huì)根據(jù)當(dāng)前的
Authentication
對(duì)象蹄溉、secureObject
和 Collection<ConfigAttribute>
來(lái)做出是否允許訪問(wèn)的選擇咨油。
AccessDecisionManager
的三個(gè)實(shí)現(xiàn),其實(shí)就是三種根據(jù) voter
結(jié)果來(lái)決定最終結(jié)果的策略柒爵,分別是 AffirmativeBased
役电、ConsensusBased
和
UnanimousBased
。策略顧名思義棉胀,就不解釋了法瑟。
AfterInvocationManager
之前沒(méi)有講 after invocation handling
的部分,是覺(jué)得不重要唁奢,使用場(chǎng)景不多(其實(shí)是自己沒(méi)遇到)■現(xiàn)在想講一講,是因?yàn)榘l(fā)現(xiàn)
spring-security-acl
使用到了 after invocation handling
的機(jī)制麻掸。那么我們就來(lái)看看 AfterInvocationManager
是怎么工作的酥夭。
acl
的部分涉及一些新的概念,準(zhǔn)備單獨(dú)寫(xiě)一篇。
通過(guò)這個(gè)圖熬北,我們可以清楚的了解到疙描,AfterInvocationManager
也只是一個(gè)接口。 它的實(shí)現(xiàn) AfterInvocationProviderManager
則是管理了“很多的” AfterInvocationProvider
來(lái)真正的執(zhí)行權(quán)限驗(yàn)證的操作讶隐。
這里“很多的”
AfterInvocationProvider
其實(shí)也就只有四個(gè)個(gè)實(shí)現(xiàn)起胰,其中三個(gè)都是acl
,包括圖里的這兩個(gè)整份。
剩下的那個(gè) PostInvocationAdviceProvider
其實(shí)也沒(méi)有真正進(jìn)行
authorization 操作待错,而是代理給了 PostInvocationAuthorizationAdvice
處理。 而這個(gè) PostInvocationAuthorizationAdvice
也只有
ExpressionBasedPostInvocationAdvice
這一個(gè)實(shí)現(xiàn)烈评,也就是基于 SpEL
表達(dá)式來(lái)進(jìn)行 authorization 的實(shí)現(xiàn)火俄。
而上面提到的所有的 manager 和 provider,都提供了 decide
方法用來(lái)做權(quán)限驗(yàn)證讲冠。 與 AccessDecisionManager.decide()
相比瓜客,這些方法多了一個(gè) returnedObject
參數(shù)。
這既是因?yàn)樗枰鳛榕袛鄺l件參與到?jīng)Q策過(guò)程中竿开,也是因?yàn)樗赡軙?huì)在決策過(guò)程中被處理谱仪,然后返回一個(gè)新的
returnedObject
作為處理后的結(jié)果。
ConfigAttribute
這個(gè)類是用來(lái)存儲(chǔ)我們的 Security 的配置的否彩。
舉個(gè)例子疯攒,下面的代碼就會(huì)生成相應(yīng)的 ConfigAttribute
:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("hello")
.hasAuthority("test")
.anyRequest()
.authenticated();
}
上面的代碼定義了:
訪問(wèn)
/hello
的請(qǐng)求需要具有test
權(quán)限其他任意請(qǐng)求,需要通過(guò)身份驗(yàn)證(不允許匿名訪問(wèn))
這樣我們就能得到這樣的 ConfigAttribute
:
這是 FilterSecurityInterceptor
的截圖列荔。 其中的
securityMetadataSource
存儲(chǔ)了很多的 ConfigAttribute
敬尺。
AbstractSecurityInterceptor
通過(guò)子類實(shí)現(xiàn)的
obtainSecurityMetadataSource
方法獲取到它,然后通過(guò)它獲取到本次使用的
Collection<ConfigAttribute>
贴浙。
截圖中的 requestMap
保存了 RequestMatcher
與
Collection<ConfigAttribute>
的關(guān)系砂吞。
當(dāng)我們請(qǐng)求 /hello
時(shí),就會(huì)得到第一個(gè)
Collection<ConfigAttribute>
崎溃,也就是包含了 hasAuthority('test')
的那一個(gè)蜻直。 當(dāng)我們請(qǐng)求其他接口時(shí),就會(huì)得到第二個(gè)袁串。
接著概而,這些被獲取到的 ConfigAttribute
就可以被后續(xù)的驗(yàn)證邏輯使用到。
總結(jié)
本文介紹了 Spring Security Authorization囱修,并著重介紹了
FilterSecurityInterceptor
如何在 SecurityFilterChain
的最后使用
AccessDecisionManager
和 AfterInvocationManager
來(lái)實(shí)現(xiàn)
pre-invocation handling 和 after invocation handling到腥。
對(duì)于 AccessDecisionManager
和
AfterInfocationManager
,則沒(méi)有詳細(xì)介紹內(nèi)部的邏輯蔚袍,而是介紹了它們?nèi)绾卫米宇惡推渌涌趤?lái)完成權(quán)限驗(yàn)證的。其內(nèi)部具體的細(xì)節(jié)邏輯,讀者可以自己研究啤咽。