前段時間分別用vue和react寫了兩個后臺管理系統(tǒng)的模板vue-quasar-admin和3YAdmin。兩個項目中都實現(xiàn)了基于RBAC的權限控制。因為本職工作是后端開發(fā),比較清楚權限控制一個管理系統(tǒng)應該必須具備的核心功能,而且是可以做到通用的。打算寫寫關于管理系統(tǒng)前后端分離方面的文章,也是做一個知識的總結(jié),其中會涉及到vue,react,node,.net core等方面的知識。
術語描述
- 用戶(Subject):發(fā)起操作的主體
- 對象(Object):指操作所針對的客體對象,比如文章或評論
- 權限(Permission):用來指代對某種對象的某一種操作,例如“添加文章的操作”
- 權限碼:權限的代號,例如用“ARTICLE_ADD”來指代“添加文章的操作”權限
權限有時候也可以稱為動作或者功能。比如“添加文章”,既可以認為它是一個動作,也可以認為它是一個功能。對象也可以稱為資源。
常用的權限模型
- ACL(Access Control List)(訪問控制列表)
- DAC(Discretionary Access Control)(自主訪問控制)
- MAC(Mandatory Access Control)(強制訪問控制)
- RBAC(Role-Based Access Control)(基于角色的訪問控制)
- ABAC(Attribute-Based Access Control)(基于屬性的訪問控制)
ACL(Access Control List)(訪問控制列表)
ACL是最早也是最基本的一種訪問控制機制,它是用來描述用戶和權限之間關系的數(shù)據(jù)列表。它的原理非常簡單:每一項資源,都配有一個列表,這個列表記錄的就是哪些用戶可以對這項資源執(zhí)行CRUD等操作。當試圖訪問這項資源時,會首先檢查這個列表中是否有關于當前用戶的訪問權限,從而確定當前用戶可否執(zhí)行相應的操作。
例如一個文件對象的 ACL 為 Alice: read,write; Bob: read,這代表 Alice 對該文件既能讀又能寫,而 Bob 只能讀取。
由于ACL的簡單性,使得它幾乎不需要任何基礎設施就可以完成訪問控制。但同時它的缺點也是很明顯的,由于需要維護大量的訪問權限列表,ACL在性能上有明顯的缺陷。另外,對于擁有大量用戶與眾多資源的應用,管理訪問控制列表本身就變成非常繁重的工作。
最開始的ACL定義中,用戶直接和權限掛鉤,數(shù)據(jù)存儲的是用戶與權限的關聯(lián)關系。如果兩個用戶的權限是一樣的,那么就需要分別存儲這兩個用戶與權限的關聯(lián)關系,也是上面所提到的ACL的缺陷。為了解決這些問題,便有了對ACL設計的改進,相同權限的用戶放到同一個分組里,分組與權限掛鉤,不再是用戶直接與權限掛鉤。以及后來出現(xiàn)的RBAC(基于角色的訪問控制),角色與分組也是差不多的概念,角色直接與權限掛鉤,用戶再與角色進行關聯(lián)。
所以,現(xiàn)在一般說ACL,不再是用戶直接和權限掛鉤的一種權限控制模型,把它看做一個單純的訪問控制列表即可。列表里維護的可能是用戶與權限的關系,也可以是用戶組與權限的關系,也可以是角色與權限的關系,甚至是部門,職位等等于權限的關系。
ACL是權限體系中的業(yè)務規(guī)則。RBAC等權限模型要用到ACL才能工作,ACL服務于RBAC等權限模型,其它權限控制體系里的權限規(guī)則也叫ACL。
DAC(Discretionary Access Control)(自主訪問控制)
系統(tǒng)會識別用戶,然后根據(jù)被操作對象(Subject)的權限控制列表(ACL: Access Control List)或者權限控制矩陣(ACL: Access Control Matrix)的信息來決定用戶的是否能對其進行哪些操作,例如讀取或修改。
而擁有對象權限的用戶,又可以將該對象的權限分配給其他用戶,所以稱之為“自主(Discretionary)”控制。
因為用戶能自主地將自己擁有的權限授予其他用戶,所以DAC模型可以任意傳遞權限,用戶能間接獲得本不具有的訪問權限,因此DAC模型的安全性較低,不能給系統(tǒng)充分的數(shù)據(jù)保護。
DAC可以直接使用ACL的物理模型,區(qū)別在于,DAC模型中用戶可以將自己具備的權限分配給其它用戶(程序里的操作就是根據(jù)用戶ID篩選出權限列表,根據(jù)列表為要分配權限的用戶構(gòu)造出新的權限列表并保存)
DAC是傳統(tǒng)的UNIX訪問控制模型,也是Windows文件系統(tǒng)的訪問控制模型。
Windows的文件訪問權限的設置中,除了用戶,還有組。這個組與后面要說到的RABC模型的角色有什么區(qū)別呢?
https://stackoverflow.com/questions/7770728/group-vs-role-any-real-difference
我認為沒必要去劃分的太清楚,不管是組還是角色,都是為了更好的管理和分配權限在最原始的ACL模型上做的改進。如果有需要,甚至可以把權限分配到部門,職位上。
MAC(Mandatory Access Control)(強制訪問控制)
MAC是為了彌補DAC權限控制過于分散的問題而誕生的。在MAC的設計中,每一個對象都有一些權限標識,每個用戶同樣也會有一些權限標識,而用戶能否對該對象進行操作取決于雙方的權限標識的關系,這個限制判斷通常是由系統(tǒng)硬性限制的。訪問時,系統(tǒng)先對用戶的訪問許可級別和資源對象的密級進行比較,再決定用戶是否可以訪問資源對象。用戶不能改變自身和資源對象的安全級別,只有系統(tǒng)管理員或管理程序才能 控制資源對象和用戶的級別。比如在影視作品中我們經(jīng)常能看到特工在查詢機密文件時,屏幕提示需要“無法訪問,需要一級安全許可”,這個例子中,文件上就有“一級安全許可”的權限標識,而用戶并不具有。
MAC非常適合機密機構(gòu)或者其他等級觀念強烈的行業(yè),但對于類似商業(yè)服務系統(tǒng),則因為不夠靈活而不能適用。
MAC可以繼續(xù)使用DAC的模型,但是要對用戶進行等級劃分,比如一級,二級,三級。。。,對對象資源也要做劃分,比如機密,秘密和最高機密。用戶訪問的資源的時候,根據(jù)用戶等級與資源訪問級別來做判斷,比如一級用戶只能訪問機密文件,如果訪問的是最高機密文件,系統(tǒng)就會拒絕。這一系列規(guī)則是優(yōu)先于DAC的,如果MAC與DAC混用,要先校驗MAC再校驗DAC。
RBAC(Role-Based Access Control)(基于角色的訪問控制)
ACL的訪問控制機制中,直接維護的是用戶與功能的關系,這一系列的關系就是一個權限列表。當很多的用戶具有相同功能權限的時候,就要進行繁瑣的關聯(lián)操作。RBAC就是在用戶與權限之間引入了角色的概念。用戶與角色之間做關聯(lián),權限列表維護的是角色與功能的關系。
RBAC是目前使用最普遍的權限控制模型。當某些用戶具備相同的權限的時候,只需要為這些用戶建一個角色,把相應的功能關聯(lián)到這個角色上,生成角色的權限列表。當有新的用戶需要相同權限的時候,把用戶關聯(lián)到這個角色上即可。而當用檢查或校驗用戶的操作權限的時候,查詢用戶所屬角色的權限列表即可。
當然,RBAC也不是完美的,比如想要為某個用戶單獨設置某個功能權限,可能需要為這個功能權限單獨創(chuàng)建一個角色,然后把特定的用戶關聯(lián)到這個角色上。當想要移除某個用戶的特定功能權限的時候,可能需要重新設置角色的功能權限,把特定功能權限從當前角色中移除,建立新的角色并關聯(lián)特定的功能權限,然后再把新角色與相關的用戶做關聯(lián)(也可以直接在特定功能的程序里校驗操作用戶)
這里說一個比較常見的RBAC的錯誤的用法:那就是直接使用角色做權限判斷。比如只有角色A才能做文章的刪除操作。
function delPost(postId){
if(!isRole('A')){
return false;
}
}
如果需求該為角色B也可以刪除文章。那就必須修改代碼
function delPost(postId){
if(!isRole('A')&&!isRole('B')){
return false;
}
}
正確的做法應該是添加"刪除文章"這個功能,把這個功能關聯(lián)到相應的角色上。判斷的時候是根據(jù)功能去判斷而不是角色。
function delPost(postId){
if(!hasPermission('POST_DEL')){
return false;
}
}
針對“只有角色A才能做文章的刪除操作”這一需求,把這個刪除功能關聯(lián)到角色A上,然后把需要這個操作權限的用戶加入到角色A中即可。當別的角色也需要這個操作權限,把功能關聯(lián)到對應角色上即可,不需要再修改代碼。
在RBAC的核心基礎上,還可以做相應的擴展,比如角色繼承,角色分組之類的,這些擴展都是為了在一定程度簡化權限管理工作。
ABAC(Attribute-Based Access Control)(基于屬性的權限控制)
RBAC雖然是目前最普遍的權限控制模型。但是某些情況下,RBAC是無法滿足并且也實現(xiàn)不了的。比如業(yè)務員1和業(yè)務員2都屬于業(yè)務員角色,都有查看客戶訂單的權限。當有一個需求,要求業(yè)務員1只能查看北京地區(qū)的客戶的訂單,業(yè)務員2只能查看上海的客戶的訂單。這單單使用RBAC是無法實現(xiàn)。借助RBAC,可行的做法是,分地區(qū)創(chuàng)建角色,然后程序中根據(jù)角色做數(shù)據(jù)的過濾,這種做法缺點之前也提到過,需求變更的時候可能需要每次都修改代碼。
上面業(yè)務員查看訂單的例子,地區(qū)是訂單的一個屬性,需求就是針對這個地區(qū)屬性來做訂單的查詢范圍的權限控制。這種權限控制方式就是ABAC(Attribute-Based Access Control)(基于屬性的權限控制),也被一些人稱為是權限系統(tǒng)設計的未來。
不同于常見的將用戶通過某種方式關聯(lián)到權限的方式,ABAC則是通過動態(tài)計算一個或一組屬性是否滿足某種條件來進行授權判斷的(可以編寫簡單的邏輯)。屬性通常來說分為四類:用戶屬性(如用戶年齡),環(huán)境屬性(如當前時間),操作屬性(如讀取)和對象屬性(如一篇文章,又稱資源屬性),所以理論上能夠?qū)崿F(xiàn)非常靈活的權限控制,幾乎能滿足所有類型的需求。
例如規(guī)則:“允許所有班主任在上課時間自由進出校門”這條規(guī)則,其中,“班主任”是用戶的角色屬性,“上課時間”是環(huán)境屬性,“進出”是操作屬性,而“校門”就是對象屬性了。
ABAC非常的靈活,但是實現(xiàn)也是非常的難。這其中涉及到邏輯的動態(tài)執(zhí)行,數(shù)據(jù)動態(tài)過濾等,更加具體就是動態(tài)拼接SQL語句(使用ORM的話就是動態(tài)組裝對應ORM的查詢語句)。
感興趣的可以在Github上搜索ABAC,看看不同語言是否已經(jīng)有現(xiàn)成的解決方案。下面說說我學習到的一種實現(xiàn)方式:
還是業(yè)務員查看訂單的例子,在RBAC的基礎上,擴展一個實體規(guī)則,訂單就是實體,也就是針對訂單設置一系列的規(guī)則。規(guī)則存儲格式可以是json也可以是xml,甚至是Sql語句,能解析即可。比如北京地區(qū)這個規(guī)則:
{
"regionId":1
}
上海地區(qū):
{
"regionId":3
}
regionId
就是系統(tǒng)里對應區(qū)域的Id,也是訂單或訂單相關表的某個字段。
保存這個規(guī)則的時候,規(guī)則內(nèi)容(就是上面的json),規(guī)則實體(也就是訂單,表明這個規(guī)則是針對訂單的)是必須的。也可以加上這個規(guī)則是適用增刪改查中的一種或多種。
創(chuàng)建好實體的規(guī)則,將規(guī)則與角色做關聯(lián),也就是將北京地區(qū)的規(guī)則關聯(lián)到北京地區(qū)角色上,上海地區(qū)的規(guī)則關聯(lián)到上海地區(qū)角色上。
后端做權限校驗的時候,還是先按RBAC模型的控制方式進行校驗(是否具備訂單查看權限),然后根據(jù)當前操作對象(也就是實體),取出用戶所屬角色關聯(lián)的對應實體的規(guī)則。然后解析規(guī)則,動態(tài)拼接Sql或者ORM語句。
沒做地區(qū)限制(或沒配置規(guī)則)的時候,Sql可能是
select userId,orderNo,createdDate from T_Order
配置了規(guī)則,解析拼接后可能就是
select userId,orderNo,createdDate from T_Order where regionId=1
這里是針對地區(qū)這個屬性實現(xiàn)了動態(tài)的權限控制。實際開發(fā)過程中,要控制的東西是非常多了,查看字段的控制,數(shù)據(jù)范圍的控制。要滿足這些復雜的控制,需要制定一套完整的規(guī)則,以及針對規(guī)則編寫相應的解析程序。比如根據(jù)配置的規(guī)則,最后解析出來可能是各種Sql語句:<,>,=,like,in,not in等等。
可以看出,要真正的落地實現(xiàn)ABAC是多么的復雜。每次都要解析規(guī)則,對程序的性能也造成的影響,就算使用緩存,命中的概率也是非常的小,因為很多因素都是動態(tài)的。
所以,如果需要根據(jù)屬性做權限判斷的場景不是很多的話,還是建議使用RBAC,然后程序中做判斷比較省事省力。
總結(jié)
ACL早期定義中是一種權限控制機制,這種機制直接維護的是用戶與功能的關系,功能就是針對對象定義的一些操作,比如增刪改查的等。用戶與功能的關系列表也稱為權限列表或訪問控制列表,現(xiàn)在說ACL,一般就是指這個權限列表或訪問控制列表,但是里面維護的關系不一定是用戶與功能的關系,在RBAC中維護的就是角色與功能的關系。
RBAC在ACL的基礎上加入了角色的概念,權限列表或訪問控制列表里維護的不再是用戶與功能的關系,而是角色與功能的關系。ACL可以和RBAC混著用,既可以在角色上設置權限,也可以直接給用戶設置權限,更加靈活。借助角色的思想,可以在用戶組,組織,職位等等上設置權限,以便更好的做好權限管理,也就是將權限設置從單一個體轉(zhuǎn)移到某一類組合上。
ABAC非常的靈活,也非常的難實現(xiàn)。