[认证授权] 6.Permission Based Access Control
在前面5篇博客中介紹了OAuth2和OIDC(OpenId Connect),其作用是授權(quán)和認(rèn)證。那么當(dāng)我們得到OAuth2的Access Token或者OIDC的Id Token之后,我們的資源服務(wù)如何來(lái)驗(yàn)證這些token是否有權(quán)限來(lái)執(zhí)行對(duì)資源的某一項(xiàng)操作呢?比如我有一個(gè)API,/books,它具有如下5個(gè)操作:
| POST /books | 添加一本書(shū) |
| GET /books/{id} | 獲取一本書(shū) |
| PUT /books/{id} | 更新一本書(shū) |
| DELETE /books/{id} | 刪除一本書(shū) |
| GET /books | 獲取書(shū)的列表 |
其偽代碼如下:
[Route("books")]
public class BooksController : Controller
{[HttpGet("")]public Book[] Get() { return null; }[HttpGet("{bookId}")]public Book Get(int bookId) { return null; }[HttpPost("")]public Book Post(Book book) { return null; }[HttpPut("{bookId}")]public Book Put(int bookId, Book book) { return null; }[HttpDelete("{bookId}")]public Book Delete(int bookId) { return null; }
} 那么我們先看看基于OAuth2的Access Token,OIDC的Id Token和傳統(tǒng)的基于角色的權(quán)限控制是如何處理控制這些資源的操作。
1 OAuth2的Access Token之Scope
我們都知道OAuth2的最終產(chǎn)物是提供給我們一個(gè)Access Token,而這個(gè)Access Token中包含了一個(gè)Scope的字段,這個(gè)字段代表的是授權(quán)服務(wù)器或者資源擁有者授予第三方客戶(hù)端允許操作資源服務(wù)器的哪些資源的范圍。這里有一點(diǎn)需要注意的是,這個(gè)授權(quán)過(guò)程可以有資源擁有著的參與(Authorization Code,Implicit,Resource Owner Password Credentials Grant),也可以沒(méi)有他的參與(Client Credentials Grant)。那么基于上述的books的資源,我們可以定義一個(gè)?user_manager?的Scope,來(lái)控制對(duì)books的五個(gè)操作的權(quán)限控制。那么Books的基于Scope的權(quán)限控制看起來(lái)就像是這樣的:
[Route("books")]
public class BooksController : Controller
{[HttpGet("")][Scope("book_manager")]public Book[] Get() { return null; }[HttpGet("{bookId}")][Scope("book_manager")]public Book Get(int bookId) { return null; }[HttpPost("")][Scope("book_manager")]public Book Post(Book book) { return null; }[HttpPut("{bookId}")][Scope("book_manager")]public Book Put(int bookId, Book book) { return null; }[HttpDelete("{bookId}")][Scope("book_manager")]public Book Delete(int bookId) { return null; }
} 注意看紅色的部分,為每一個(gè)操作都添加了一個(gè)Scope的描述。如果Access Token擁有user_manager這個(gè)Scope(不管他是OAuth2的哪一個(gè)授權(quán)方式頒發(fā)的,我們的最終代碼部分只認(rèn)Scope),那么對(duì)這些API的調(diào)用就是被允許的,否則視為無(wú)權(quán)操作。
2 OIDC的Id Token之sub
關(guān)于Id Token的用途以及其包含哪些信息請(qǐng)參考Id Token。Id Token和Access Token的不同之處在于它一定是包含某一個(gè)用戶(hù)的標(biāo)識(shí)?sub?,但是沒(méi)有Scope,這是因?yàn)镮d Token的用途是認(rèn)證當(dāng)前用戶(hù)是誰(shuí),所以用戶(hù)是必須存在的;由于僅僅是認(rèn)證,則不會(huì)包含被認(rèn)證用戶(hù)可以做哪些操作之類(lèi)的授權(quán)相關(guān)的事情。那么針對(duì)Id Token,我們的API應(yīng)該如何進(jìn)行權(quán)限管控呢?通常的做法是使用傳統(tǒng)的基于校色的權(quán)限控制(Role Based Access Control)。其實(shí)現(xiàn)細(xì)節(jié)就不解釋了,它的模型大致是:一個(gè)實(shí)體(用戶(hù)或者組織)擁有一組角色,每一個(gè)角色代表著一組權(quán)限集合。感覺(jué)是不是和Scope很像呢,其實(shí)差不多。我們定義一個(gè)這樣的角色?圖書(shū)管理員?吧。這里是故意和Scope的命名區(qū)分開(kāi)的,因?yàn)槠鋪?lái)源不同,那么我們最終實(shí)現(xiàn)的時(shí)候也會(huì)是獨(dú)立開(kāi)來(lái)的。
1 [Route("books")]2 public class BooksController : Controller3 {4 [HttpGet("")]5 [Scope("book_manager")]6 [Role("圖書(shū)管理員")]7 public Book[] Get() { return null; }8 9 [HttpGet("{bookId}")]
10 [Scope("book_manager")]
11 [Role("圖書(shū)管理員")]
12 public Book Get(int bookId) { return null; }
13
14 [HttpPost("")]
15 [Scope("book_manager")]
16 [Role("圖書(shū)管理員")]
17 public Book Post(Book book) { return null; }
18
19 [HttpPut("{bookId}")]
20 [Scope("book_manager")]
21 [Role("圖書(shū)管理員")]
22 public Book Put(int bookId, Book book) { return null; }
23
24 [HttpDelete("{bookId}")]
25 [Scope("book_manager")]
26 [Role("圖書(shū)管理員")]
27 public Book Delete(int bookId) { return null; }
28 } 如果?sub?代表的用戶(hù)自身?yè)碛谢蛘咂渌鶎俚慕M織機(jī)構(gòu)擁有(不管其是怎么組織管理的吧,最終我們可以知道這個(gè)用戶(hù)是否具有某一個(gè)角色)?圖書(shū)管理員?這個(gè)角色。則允許其訪(fǎng)問(wèn)books的這些操作。
3 以上兩種方式的弊端在哪里?
其實(shí)不止以上兩種,比如在Asp.Net Core中有內(nèi)置的這些授權(quán)控制組件:
1 [Authorize(Policy = "AtLeast21")]
2 public class AlcoholPurchaseController : Controller
3 {
4 public IActionResult Login() => View();
5
6 public IActionResult Logout() => View();
7 } 以上這些本質(zhì)上和上面的基于Scope和基于Role的屬于同一種類(lèi)型。我們這樣做當(dāng)然可以工作,但是問(wèn)題來(lái)了,它們直觀(guān)嗎,靈活嗎?繁瑣嗎?好用嗎?能滿(mǎn)足我們變化的需求嗎?總有著一種把簡(jiǎn)單的事情搞復(fù)雜的感覺(jué)。比如現(xiàn)在我增需要增加一個(gè)角色,超級(jí)管理員,那么上述的代碼是不是需要我們做出改變呢?
1 [HttpGet("")]
2 [Scope("book_manager")]
3 [Role("圖書(shū)管理員","超級(jí)管理員")]
4 public Book[] Get() { return null; } 再比如,現(xiàn)在需要增加一個(gè)Scope?book_reader?,它只能執(zhí)行讀取的操作,又要做出改變了吧。況且即使我們把Scope和Role合二為一了,還是混亂不堪。
4 基于權(quán)限為最小粒度的解決方案
那么造成這些問(wèn)題的根本原因是什么?答:不管是Scope還是Role它們體現(xiàn)的都是一個(gè)隱式的描述信息,而不是某一個(gè)具體的操作行為的描述信息。既然我們知道了其癥結(jié)所在,那么怎么解決這個(gè)問(wèn)題呢?原理很簡(jiǎn)單,使用權(quán)限作為我們的最小單元,把Scope和Role等等還有其他的一些管理組織權(quán)限的概念都作為一個(gè)中間層,禁止它們出現(xiàn)在接口權(quán)限驗(yàn)證的地方,而是僅作為管理組織Permission的手段存在。然后改造上面的代碼如下:
1 [Route("books")]2 public class BooksController : Controller3 {4 [HttpGet("")]5 [Permission("books.read")]6 public Book[] Get() { return null; }7 8 [HttpGet("{bookId}")]9 [Permission("book.read")]
10 public Book Get(int bookId) { return null; }
11
12 [HttpPost("")]
13 [Permission("book.add")]
14 public Book Post(Book book) { return null; }
15
16 [HttpPut("{bookId}")]
17 [Permission("book.edit")]
18 public Book Put(int bookId, Book book) { return null; }
19
20 [HttpDelete("{bookId}")]
21 [Permission("book.delete")]
22 public Book Delete(int bookId) { return null; }
23 } 我們把每一個(gè)操作都定義一個(gè)權(quán)限Permission,不管你是Access Token的Scope,還是Role,都不會(huì)在這里出現(xiàn)。比如在檢查超級(jí)管理員是不是能操作的時(shí)候,我們可以直接放行(把這些檢查和我們對(duì)接口的操作權(quán)限的描述分開(kāi))。如果是名為book_reader的Scope的時(shí)候,我們讓book_reader只關(guān)聯(lián)books.read和book.read這兩個(gè)Permission,而這種關(guān)聯(lián)關(guān)系的管理,我們是可以通過(guò)數(shù)據(jù)存儲(chǔ)來(lái)維持的,也很方便的提供管理頁(yè)面來(lái)靈活的配置。而最終的代碼上關(guān)心的只是Permission。這種方式可以稱(chēng)為Resource Based Access Control或者Permission Based Access Control。
5 Apache Shiro
以上是我自己的一些理解和思路,然后我發(fā)現(xiàn)了Apache Shiro這個(gè)項(xiàng)目,感覺(jué)就像是找到了組織,Apache Shiro走的更遠(yuǎn),而且為Permission定義了一套規(guī)則。強(qiáng)烈建議讀一讀https://shiro.apache.org/permissions.html這篇文檔。而.Net這邊就沒(méi)有這么好的福氣了,,,Asp.Net Core中的默認(rèn)授權(quán)過(guò)濾器還是傳統(tǒng)的方式。
?
不過(guò)基于Asp.Net Core的Filter:IAuthorizationFilter,我們可以把這一整套授權(quán)控制方式給替換掉:使用代碼:https://github.com/linianhui/oidc.example/tree/master/src/web.oauth2.resources;Filters代碼:https://github.com/linianhui/oidc.example/tree/master/src/aspnetcore.filters.permissions。
從此和討厭的?[Authorize(Roles ="圖書(shū)管理員",Policy ="XXX")]?說(shuō)再見(jiàn)。
以上只是個(gè)人的一些理解,如有錯(cuò)誤,歡迎指正。
參考
https://shiro.apache.org/
強(qiáng)烈推薦:https://shiro.apache.org/permissions.html
https://stormpath.com/blog/new-rbac-resource-based-access-control
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/
作者:Blackheart 原文鏈接:http://www.cnblogs.com/linianhui/p/permission-based-access-control.html轉(zhuǎn)載于:https://www.cnblogs.com/gongap/p/9337768.html
總結(jié)
以上是生活随笔為你收集整理的[认证授权] 6.Permission Based Access Control的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 求一个霸气的微信网名
- 下一篇: 2018考研英语:10篇必背的真题文章