權限系統設計模型分析(DAC,MAC,RBAC,ABAC)

此篇文章主要嘗試將世面上現有的一些權限系統設計做一下簡單的總結分析,個人水平有限,如有錯誤請不吝指出。

術語

這裏對後面會用到的詞彙做一個說明,老司機請直接翻到常見設計模式

用戶

發起操作的主體。

對象(Subject)

指操作所針對的客體對象,比如訂單數據或圖片文件。

權限控制表 (ACL: Access Control List)

用來描述權限規則或用戶和權限之間關係的數據表。

權限 (Permission)

用來指代對某種對象的某一種操作,例如“添加文章的操作”。

權限標識

權限的代號,例如用“ARTICLE_ADD”來指代“添加文章的操作”權限。

常見設計模式

自主訪問控制(DAC: Discretionary Access Control)

系統會識別用戶,然後根據被操作對象(Subject)的權限控制列表(ACL: Access Control List)或者權限控制矩陣(ACL: Access Control Matrix)的信息來決定用戶的是否能對其進行哪些操作,例如讀取或修改。

而擁有對象權限的用戶,又可以將該對象的權限分配給其他用戶,所以稱之爲“自主(Discretionary)”控制。

這種設計最常見的應用就是文件系統的權限設計,如微軟的NTFS。

DAC最大缺陷就是對權限控制比較分散,不便於管理,比如無法簡單地將一組文件設置統一的權限開放給指定的一羣用戶。

Windows的文件權限

強制訪問控制(MAC: Mandatory Access Control)

MAC是爲了彌補DAC權限控制過於分散的問題而誕生的。在MAC的設計中,每一個對象都都有一些權限標識,每個用戶同樣也會有一些權限標識,而用戶能否對該對象進行操作取決於雙方的權限標識的關係,這個限制判斷通常是由系統硬性限制的。比如在影視作品中我們經常能看到特工在查詢機密文件時,屏幕提示需要“無法訪問,需要一級安全許可”,這個例子中,文件上就有“一級安全許可”的權限標識,而用戶並不具有。

MAC非常適合機密機構或者其他等級觀念強烈的行業,但對於類似商業服務系統,則因爲不夠靈活而不能適用。

RedHat MLS

Red Hat: MLS

基於角色的訪問控制(RBAC: Role-Based Access Control)

因爲DAC和MAC的諸多限制,於是誕生了RBAC,並且成爲了迄今爲止最爲普及的權限設計模型。

RBAC在用戶和權限之間引入了“角色(Role)”的概念(暫時忽略Session這個概念):

RBAC核心設計

圖片來自Apache Directory

如圖所示,每個用戶關聯一個或多個角色,每個角色關聯一個或多個權限,從而可以實現了非常靈活的權限管理。角色可以根據實際業務需求靈活創建,這樣就省去了每新增一個用戶就要關聯一遍所有權限的麻煩。簡單來說RBAC就是:用戶關聯角色,角色關聯權限。另外,RBAC是可以模擬出DAC和MAC的效果的。

例如數據庫軟件MongoDB便是採用RBAC模型,對數據庫的操作都劃分成了權限(MongoDB權限文檔):

權限標識 說明
find 具有此權限的用戶可以運行所有和查詢有關的命令,如:aggregate、checkShardingIndex、count等。
insert 具有此權限的用戶可以運行所有和新建數據有關的命令:insert和create等。
collStats 具有此權限的用戶可以對指定database或collection執行collStats命令。
viewRole 具有此權限的用戶可以查看指定database的角色信息。
 

基於這些權限,MongoDB提供了一些預定義的角色(MongoDB預定義角色文檔,用戶也可以自己定義角色):

角色 find insert collStats viewRole
read    
readWrite  
dbAdmin    
userAdmin      

最後授予用戶不同的角色,就可以實現不同粒度的權限分配了。

目前市面上絕大部分系統在設計權限系統時都採用RBAC模型。然而也有的系統錯誤地實現了RBAC,他們採用的是判斷用戶是否具有某個角色而不是判斷權限,例如以下代碼:

<?php

if ($user->hasRole('hr')) {
    // 執行某種只有“HR”角色才能做的功能,例如給員工漲薪…
    // ...
}

如果後期公司規定部門經理也可以給員工漲薪,這時就不得不修改代碼了。

以上基本就是RBAC的核心設計(RBAC Core)。而基於核心概念之上,RBAC規範還提供了擴展模式。

角色繼承(Hierarchical Role)

RBAC 1

帶有角色繼承的RBAC。圖片來自Apache Directory

顧名思義,角色繼承就是指角色可以繼承於其他角色,在擁有其他角色權限的同時,自己還可以關聯額外的權限。這種設計可以給角色分組和分層,一定程度簡化了權限管理工作。

職責分離(Separation of Duty)

爲了避免用戶擁有過多權限而產生利益衝突,例如一個籃球運動員同時擁有裁判的權限(看一眼就給你判犯規狠不狠?),另一種職責分離擴展版的RBAC被提出。

職責分離有兩種模式:

  • 靜態職責分離(Static Separation of Duty):用戶無法同時被賦予有衝突的角色。
  • 動態職責分離(Dynamic Separation of Duty):用戶在一次會話(Session)中不能同時激活自身所擁有的、互相有衝突的角色,只能選擇其一。

RBAC 2

靜態職責分離。圖片來自Apache Directory

RBAC 3

動態職責分離。圖片來自Apache Directory

講了這麼多RBAC,都還只是在用戶和權限之間進行設計,並沒有涉及到用戶和對象之間的權限判斷,而在實際業務系統中限制用戶能夠使用的對象是很常見的需求。例如華中區域的銷售沒有權限查詢華南區域的客戶數據,雖然他們都具有銷售的角色,而銷售的角色擁有查詢客戶信息的權限。

那麼我們應該怎麼辦呢?

用戶和對象的權限控制

在RBAC標準中並沒有涉及到這個內容(RBAC基本只能做到對一類對象的控制),但是這裏講幾種基於RBAC的實現方式。

首先我們看看PHP框架Yii 1.X的解決方案(2.X中代碼更爲優雅,但1.X的示例代碼更容易看明白):

<?php

$auth=Yii::app()->authManager;
 
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');

// 主要看這裏。
// 這裏創建了一個名爲`updateOwnPost`的權限,並且寫了一段代碼用來檢驗用戶是否爲該帖子的作者
$bizRule='return Yii::app()->user->id==$params["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);
$task->addChild('updatePost');
 
$role=$auth->createRole('reader');
$role->addChild('readPost');
 
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
 
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
 
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');

實現效果:

Yii 1.X權限圖

圖片來自Yii官方WiKi

在這個Yii的官方例子中,updateOwnPost在判斷用戶是否具有updatePost權限的基礎上更進一步判斷了用戶是否有權限操作這個特定的對象,並且這個判斷邏輯是通過代碼設置的,非常靈活。

不過大部分時候我們並不需要這樣的靈活程度,會帶來額外的開發和維護成本,而另一種基於模式匹配規則的對象權限控制可能更適合。例如判斷用戶是否對Id爲123的文章具有編輯的權限,代碼可能是這樣的:

<?php

// 假設articleId是動態獲取的
$articleId = 123;

if ($user->can("article:edit:{$articleId}")) {
    // ...
}

而給用戶授權則有多種方式可以選擇:

<?php

// 允許用戶編輯Id爲123的文章
$user->grant('article:edit:123');

// 使用通配符,允許用戶編輯所有文章
$user->grant('article:edit:*');

雖然不及Yii方案的靈活,但某些場景下這樣就夠用了。

如果大家還有更好的方案,歡迎在評論中提出。

基於屬性的權限驗證(ABAC: Attribute-Based Access Control)

ABAC被一些人稱爲是權限系統設計的未來。

不同於常見的將用戶通過某種方式關聯到權限的方式,ABAC則是通過動態計算一個或一組屬性來是否滿足某種條件來進行授權判斷(可以編寫簡單的邏輯)。屬性通常來說分爲四類:用戶屬性(如用戶年齡),環境屬性(如當前時間),操作屬性(如讀取)和對象屬性(如一篇文章,又稱資源屬性),所以理論上能夠實現非常靈活的權限控制,幾乎能滿足所有類型的需求。

例如規則:“允許所有班主任在上課時間自由進出校門”這條規則,其中,“班主任”是用戶的角色屬性,“上課時間”是環境屬性,“進出”是操作屬性,而“校門”就是對象屬性了。爲了實現便捷的規則設置和規則判斷執行,ABAC通常有配置文件(XML、YAML等)或DSL配合規則解析引擎使用。XACML(eXtensible Access Control Markup Language)是ABAC的一個實現,但是該設計過於複雜,我還沒有完全理解,故不做介紹。

總結一下,ABAC有如下特點:

  1. 集中化管理
  2. 可以按需實現不同顆粒度的權限控制
  3. 不需要預定義判斷邏輯,減輕了權限系統的維護成本,特別是在需求經常變化的系統中
  4. 定義權限時,不能直觀看出用戶和對象間的關係
  5. 規則如果稍微複雜一點,或者設計混亂,會給管理者維護和追查帶來麻煩
  6. 權限判斷需要實時執行,規則過多會導致性能問題

既然ABAC這麼好,那最流行的爲什麼還是RBAC呢?

我認爲主要還是因爲大部分系統對權限控制並沒有過多的需求,而且ABAC的管理相對來說太複雜了。Kubernetes便因爲ABAC太難用,在1.8版本里引入了RBAC的方案

ABAC有時也被稱爲PBAC(Policy-Based Access Control)或CBAC(Claims-Based Access Control)。

結語

權限系統設計可謂博大精深,這篇文章只是介紹了一點皮毛。

隨着人類在信息化道路上越走越遠,權限系統的設計也在不斷創新,但目前好像處在了平臺期。

可能因爲在RBAC到ABAC之間有着巨大的鴻溝,無法輕易跨越,也可能是一些基於RBAC的微創新方案還不夠規範化從而做到普及。不過在服務化架構的浪潮下,未來這一塊必然有極高的需求,也許巨頭們已經開始佈局了。



作者:該葉無法找到
鏈接:https://www.jianshu.com/p/ce0944b4a903
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章