引言: 本文系《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)》系列的第三篇,本文重點(diǎn)講解token以及API級(jí)別的鑒權(quán)。本文對(duì)涉及到的大部分代碼進(jìn)行了分析,歡迎訂閱本系列文章。
在開始講解這一篇文章之前,先對(duì)之前兩篇文章進(jìn)行回憶下。在第一篇《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(一)》介紹了該項(xiàng)目的背景以及技術(shù)調(diào)研與最后選型。第二篇《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(二)》畫出了簡(jiǎn)要的登錄和校驗(yàn)的流程圖,并重點(diǎn)講解了用戶身份的認(rèn)證與token發(fā)放的具體實(shí)現(xiàn)。
本文重點(diǎn)講解鑒權(quán),包括兩個(gè)方面:token合法性以及API級(jí)別的操作權(quán)限。首先token合法性很容易理解,第二篇文章講解了獲取授權(quán)token的一系列流程,token是否是認(rèn)證服務(wù)器頒發(fā)的,必然是需要驗(yàn)證的。其次對(duì)于API級(jí)別的操作權(quán)限,將上下文信息不具備操作權(quán)限的請(qǐng)求直接拒絕,當(dāng)然此處是設(shè)計(jì)token合法性校驗(yàn)在先,其次再對(duì)操作權(quán)限進(jìn)行驗(yàn)證,如果前一個(gè)驗(yàn)證直接拒絕,通過(guò)則進(jìn)入操作權(quán)限驗(yàn)證。
ResourceServer配置在第一篇就列出了,在進(jìn)入鑒權(quán)之前,把這邊的配置搞清,即使有些配置在本項(xiàng)目中沒有用到,大家在自己的項(xiàng)目有可能用到。
@EnableResourceServer這個(gè)注解很重要,OAuth2資源服務(wù)器的簡(jiǎn)便注解。其使得Spring Security filter通過(guò)請(qǐng)求中的OAuth2 token來(lái)驗(yàn)證請(qǐng)求。通常與EnableWebSecurity配合使用,該注解還創(chuàng)建了硬編碼的@Order(3) WebSecurityConfigurerAdapter,由于當(dāng)前spring的技術(shù),order的順序不易修改,所以在項(xiàng)目中避免還有其他order=3的配置。
關(guān)聯(lián)的HttpSecurity,與之前的 Spring Security XML中的 "http"元素配置類似,它允許配置基于web安全以針對(duì)特定http請(qǐng)求。默認(rèn)是應(yīng)用到所有的請(qǐng)求,通過(guò)requestMatcher可以限定具體URL范圍。HttpSecurity類圖如下。
總的來(lái)說(shuō):HttpSecurity是SecurityBuilder接口的一個(gè)實(shí)現(xiàn)類,從名字上我們就可以看出這是一個(gè)HTTP安全相關(guān)的構(gòu)建器。當(dāng)然我們?cè)跇?gòu)建的時(shí)候可能需要一些配置,當(dāng)我們調(diào)用HttpSecurity對(duì)象的方法時(shí),實(shí)際上就是在進(jìn)行配置。
authorizeRequests(),formLogin()、httpBasic()這三個(gè)方法返回的分別是ExpressionUrlAuthorizationConfigurer、FormLoginConfigurer、HttpBasicConfigurer,他們都是SecurityConfigurer接口的實(shí)現(xiàn)類,分別代表的是不同類型的安全配置器。
因此,從總的流程上來(lái)說(shuō),當(dāng)我們?cè)谶M(jìn)行配置的時(shí)候,需要一個(gè)安全構(gòu)建器SecurityBuilder(例如我們這里的HttpSecurity),SecurityBuilder實(shí)例的創(chuàng)建需要有若干安全配置器SecurityConfigurer實(shí)例的配合。
關(guān)聯(lián)的ResourceServerSecurityConfigurer,為資源服務(wù)器添加特殊的配置,默認(rèn)的適用于很多應(yīng)用,但是這邊的修改至少以resourceId為單位。類圖如下。
ResourceServerSecurityConfigurer創(chuàng)建了OAuth2核心過(guò)濾器OAuth2AuthenticationProcessingFilter,并為其提供固定了OAuth2AuthenticationManager。只有被OAuth2AuthenticationProcessingFilter攔截到的oauth2相關(guān)請(qǐng)求才被特殊的身份認(rèn)證器處理。同時(shí)設(shè)置了TokenExtractor、異常處理實(shí)現(xiàn)。
OAuth2AuthenticationProcessingFilter是OAuth2保護(hù)資源的預(yù)先認(rèn)證過(guò)濾器。配合OAuth2AuthenticationManager使用,根據(jù)請(qǐng)求獲取到OAuth2 token,之后就會(huì)使用OAuth2Authentication來(lái)填充Spring Security上下文。
OAuth2AuthenticationManager在前面的文章給出的AuthenticationManager類圖就出現(xiàn)了,與token認(rèn)證相關(guān)。這邊略過(guò)貼出源碼進(jìn)行講解,讀者可以自行閱讀。
鑒權(quán)主要是使用內(nèi)置的endpoint /oauth/check_token,筆者將對(duì)端點(diǎn)的分析放在前面,因?yàn)檫@是鑒權(quán)的唯一入口。下面我們來(lái)看下該API接口中的主要代碼。
看過(guò)security-oauth源碼的同學(xué)可能立馬就看出上述代碼與源碼不同,熟悉/oauth/check_token校驗(yàn)流程的也會(huì)看出來(lái),這邊筆者對(duì)security-oauth jar進(jìn)行了重新編譯,修改了部分源碼用于該項(xiàng)目需求的場(chǎng)景。主要是加入了前置的API級(jí)別的權(quán)限校驗(yàn)。
從上面的CheckTokenEndpoint中可以看出,對(duì)于token合法性驗(yàn)證首先是識(shí)別請(qǐng)求體中的token。用到的主要方法是ResourceServerTokenServices提供的readAccessToken()方法。該接口的實(shí)現(xiàn)類為DefaultTokenServices,在之前的配置中有講過(guò)這邊配置了jdbc的TokenStore。
readAccessToken()檢索出該token值的完整信息。上述代碼比較簡(jiǎn)單,涉及到的邏輯也不復(fù)雜,此處簡(jiǎn)單講解。下圖為debug token校驗(yàn)的變量信息,讀者可以自己動(dòng)手操作下,截圖僅供參考。
至于后面的步驟,loadAuthentication()為特定的access token 加載credentials。得到的credentials 與token作為convertAccessToken()參數(shù),得到校驗(yàn)token的response。
筆者項(xiàng)目目前都是基于Web的權(quán)限驗(yàn)證,之前遺留的一個(gè)巨大的單體應(yīng)用系統(tǒng)正在逐漸拆分,然而當(dāng)前又不能完全拆分完善。為了同時(shí)兼容新舊服務(wù),盡量減少對(duì)業(yè)務(wù)系統(tǒng)的入侵,實(shí)現(xiàn)微服務(wù)的統(tǒng)一性和獨(dú)立性。筆者根據(jù)業(yè)務(wù)業(yè)務(wù)場(chǎng)景,嘗試在Auth處做操作權(quán)限校驗(yàn)。
首先想到的是資源服務(wù)器配置ResourceServer,如:
這樣做需要將每個(gè)操作接口的API權(quán)限控制放在各個(gè)不同的業(yè)務(wù)服務(wù),每個(gè)服務(wù)在接收到請(qǐng)求后,需要先從Auth服務(wù)取出該token 對(duì)應(yīng)的role和scope等權(quán)限信息。這個(gè)方法肯定是可行的,但是由于項(xiàng)目鑒權(quán)的粒度更細(xì),而且暫時(shí)不想大動(dòng)原有系統(tǒng),在加上之前網(wǎng)關(guān)設(shè)計(jì),網(wǎng)關(guān)調(diào)用Auth服務(wù)校驗(yàn)token合法性,所以最后決定在Auth系統(tǒng)調(diào)用中,把這些校驗(yàn)一起解決完。
文章開頭資源服務(wù)器的配置代碼可以看出,對(duì)于所有的資源并沒有做攔截,因?yàn)榫W(wǎng)關(guān)處是調(diào)用Auth系統(tǒng)的相關(guān)endpoint,并不是所有的請(qǐng)求url都會(huì)經(jīng)過(guò)一遍Auth系統(tǒng),所以對(duì)于所有的資源,在Auth系統(tǒng)中,定義需要鑒權(quán)接口所需要的API權(quán)限,然后根據(jù)上下文進(jìn)行匹配。這是采用的第二種方式,也是筆者目前采用的方法。當(dāng)然這種方式的弊端也很明顯,一旦并發(fā)量大,網(wǎng)關(guān)還要耗時(shí)在調(diào)用Auth系統(tǒng)的鑒權(quán)上,TPS勢(shì)必要下降很多,對(duì)于一些不需要鑒權(quán)的服務(wù)接口也會(huì)引起不可用。另外一點(diǎn)是,對(duì)于某些特殊權(quán)限的接口,需要的上下文信息很多,可能并不能完全覆蓋,對(duì)于此,筆者的解決是分兩方面:一是盡量將這些特殊情況進(jìn)行分類,某一類的情況統(tǒng)一解決;二是將嚴(yán)苛的校驗(yàn)降低,對(duì)于上下文校驗(yàn)失敗的直接拒絕,而通過(guò)的,對(duì)于某些接口,在接口內(nèi)進(jìn)行操作之前,對(duì)特殊的地方還要再次進(jìn)行校驗(yàn)。
上面在講endpoint有提到這邊對(duì)源碼進(jìn)行了改寫。CheckTokenEntity是自定義的DTO,這這個(gè)類中定義了鑒權(quán)需要的上下文,這里是指能校驗(yàn)操作權(quán)限的最小集合,如URI、roleId、affairId等等。另外定義了CheckPermissions接口,其方法checkPermission(CheckTokenEntity checkTokenEntity)返回了check的結(jié)果。而其具體實(shí)現(xiàn)類則定義在Auth系統(tǒng)中。筆者項(xiàng)目中調(diào)用的實(shí)例如下:
關(guān)于jar包spring-cloud-starter-oauth2中的具體修改內(nèi)容,大家可以看下文末筆者的GitHub項(xiàng)目。通過(guò)自定義CustomCheckPermission,覆寫checkPermission()方法,大家也可以對(duì)自己業(yè)務(wù)的操作權(quán)限進(jìn)行校驗(yàn),非常靈活。這邊涉及到具體業(yè)務(wù),筆者在項(xiàng)目中只提供接口,具體的實(shí)現(xiàn)需要讀者自行完成。
本文的源碼地址:
參考
原文鏈接:http:///2017/10/24/security3/ 本次培訓(xùn)內(nèi)容包含:Kubernetes架構(gòu)、Kubernetes安裝、Kubernetes功能導(dǎo)覽、監(jiān)控解決方案、Kubernetes高階——設(shè)計(jì)和實(shí)現(xiàn)、Kubernetes落地實(shí)踐等,點(diǎn)擊識(shí)別下方二維碼加微信好友了解具體培訓(xùn)內(nèi)容。
|
|
來(lái)自: feimishiwo > 《待分類》