這個(gè)系列文章介紹的是Identity Server 4 實(shí)施 OpenID Connect 的 Hybrid Flow.?
保護(hù)MVC客戶端:?,??
保護(hù)API資源(這里用到了RBAC: Role-based Access Control 基于角色的訪問(wèn)權(quán)限控制 ):?
本文介紹如何使用ABAC (Attribute-based Access Control 基于屬性的訪問(wèn)權(quán)限控制)保護(hù)API資源.
相關(guān)代碼:??里面04那部分.
ABAC
ABAC, Attribute-based Access Control, 基于屬性的訪問(wèn)權(quán)限控制. 有時(shí)會(huì)把它叫做CBAC, Claim-based Access Control?()或 PBAC, Policy-based Access Control?(). 它們表達(dá)的都差不多是一個(gè)意思 (盡管ASP.NET Core官方文檔把它們分成兩頁(yè)介紹).
RBAC vs ABAC
ABAC允許復(fù)雜的權(quán)限規(guī)則.
?
代碼實(shí)現(xiàn)
首先可以再添加一個(gè)國(guó)籍的IdentityResource (scope):
?
然后配置Client, 允許其請(qǐng)求上面這個(gè)scope:
?
配置API資源, 后邊我需要用到nationality和gender:
?
最后再TestUser里面添加一個(gè)nationality的claim, 再添加一個(gè)gender(性別)的claim:
(這里我添加了一個(gè)Kevin用戶, 后邊會(huì)用)
這里的gender 這個(gè)claim是在profile scope里面預(yù)定的, 所以我無(wú)需再定義一個(gè)包括gender的scope.
?
然后切換到MVC客戶端項(xiàng)目, 首先要把nationality這個(gè)scope添加到需要請(qǐng)求的scopes里面:
這樣的話國(guó)籍就可以通過(guò)用戶信息端點(diǎn)返回了.
?
由于在MVC客戶端里面需要識(shí)別出國(guó)籍這個(gè)Identity Claim, 所以需要做一下映射:
?
接下來(lái)就可以創(chuàng)建策略了, 還是在Startup的ConfigureServices里:
調(diào)用services.AddAuthorization()方法, 在它的參數(shù)里可以進(jìn)行配置.
隨后使用AddPolicy()定義了一個(gè)策略, 然后在這個(gè)方法里對(duì)這個(gè)策略進(jìn)行了配置. 它的名字是"CanViewAbout".
首先這個(gè)策略要求用戶已經(jīng)通過(guò)身份認(rèn)證, 然后國(guó)籍claim的值是"China", 性別是女性.
?
這里面使用的都是內(nèi)置的策略選項(xiàng), 適合相對(duì)不太復(fù)雜的規(guī)則.
其中RequireClaim()可以填寫多個(gè)候選值:
在這里也可以使用RequireRole()方法, 所以角色也可以參與進(jìn)來(lái).?
?
最后在MVC的HomeController的AboutAction上面:
兩種寫法都是使用的策略(Policy).
使用策略的好處就是, 規(guī)則改變的時(shí)候, 無(wú)需修改Controller里面的代碼, 只需要修改策略的配置即可.
?
下面測(cè)試一下MVC客戶端:
登錄的是Nick, 她符合策略:
?
再登入Dave試試, 他不符合策略, 所以結(jié)果是Forbidden:
?
如果需要在cshtml里面使用策略的話, 請(qǐng)使用(await AuthorizationServices.AuthorizeAsync(User, "CanViewAbout")).Succeeded, 這個(gè)方法.
不過(guò)現(xiàn)在要cshtml里面注入這個(gè)服務(wù): @inject IAuthorizationService AuthorizationService.
?
擴(kuò)展授權(quán)策略
使用內(nèi)置的策略選項(xiàng)可以處理一些比較簡(jiǎn)單的規(guī)則, 但是針對(duì)復(fù)雜一點(diǎn)的規(guī)則, 就需要對(duì)策略進(jìn)行擴(kuò)展了.
ASP.NET Core的這部分文檔介紹了這方面的內(nèi)容:?
?
用下圖解釋一下整個(gè)授權(quán)的結(jié)構(gòu):
一個(gè)Action可以附加多個(gè)授權(quán)策略, 它們必須都被滿足.
每個(gè)策略可以有多個(gè)要求(Requirement), 這些要求可以通過(guò)內(nèi)置的選項(xiàng)來(lái)制定, 也可以使用自定義的要求, 自定義的Requirement需要實(shí)現(xiàn)IAuthorizationRequirement接口.
每個(gè)Requirement都有一個(gè)或多個(gè)處理者(Handlers), 這些handlers派生于AuthorizationHandler<T>, T就是Requirement的類型. 下面要注意:
如果其中任意一個(gè)handler返回Succeed(成功), 而所有的handler都沒(méi)有返回失敗, 那么這個(gè)Requirement就被滿足了. 所以handler的處理結(jié)果有三種情況: 明確的成功, 明確的失敗, 沒(méi)有明確指出是成功還是失敗.
?
代碼實(shí)現(xiàn)
前一部分保護(hù)的是MVC客戶端, 那么這一部分就來(lái)保護(hù)API吧.
現(xiàn)在API項(xiàng)目里建立一個(gè)Requirement:
它的構(gòu)造函數(shù)可以傳遞一些參數(shù)進(jìn)來(lái), 但是我這個(gè)例子并不需要.
?
然后建立一個(gè)Handler:
里面就是一些判斷邏輯. AuthorizationHandlerContext.Resource可以轉(zhuǎn)化為AuthorizationFilterContext, 它里面有很多東西, 這個(gè)可以查看文檔.
如果它是空的, 那么就返回明確的失敗.
隨后取出用戶的gender和nationality, 分別有兩種情況可以滿足需求, 明確的設(shè)置成功. 其它的情況就直接返回, 如果有其它handler存在, 就依賴于其它handler的結(jié)果了.
但是如果這個(gè)handler成功了, 但是有其它handler是失敗的, 那么最終還是沒(méi)有滿足這個(gè)requirement.
?
最后在API的startup里面注冊(cè):
注冊(cè)Handler的時(shí)候選擇的生命周期是Singleton, 但是如果Handler里面例如注入了Repository, 那么可以生命周期可以改為Scoped.
?
最后在API的Controller里設(shè)置權(quán)限策略:
?
測(cè)試, 使用Nick和Dave都應(yīng)該可以在Contact頁(yè)面查詢出Country資源的數(shù)據(jù):
?
但是Kevin就沒(méi)有權(quán)限訪問(wèn)API了:
?
Hybrid Flow先介紹到這. 有空再介紹下Implicit....
?