[七年技術總結系列][理論篇]-RBAC許可權模型由淺入深

来源:https://www.cnblogs.com/kevinchoi/archive/2019/10/25/11716636.html
-Advertisement-
Play Games

許可權部分將分兩章介紹,第一章由淺入深介紹許可權理論知識及應用,第二章介紹具體實現。後期再講述中間件的使用時,還會插入一些許可權內容,本質上屬於中間件的應用。 許可權模塊是業務系統最常見、最基本的子集。本章假定了一個系統從最初簡單的需求到逐漸成熟且完善的許可權體系的實現過程。 閱讀本章預計花費20分鐘。 1. ...


許可權部分將分兩章介紹,第一章由淺入深介紹許可權理論知識及應用,第二章介紹具體實現。後期再講述中間件的使用時,還會插入一些許可權內容,本質上屬於中間件的應用。

許可權模塊是業務系統最常見、最基本的子集。本章假定了一個系統從最初簡單的需求到逐漸成熟且完善的許可權體系的實現過程。

閱讀本章預計花費20分鐘。

1. 最簡單的許可權模型

業務系統初期,需求簡單,對於許可權的內容本身並不複雜,我們假設許可權部分僅有這樣簡單的需求:

能給用戶賦予數據的增、刪、改、查四種許可權

分析此需求,許可權的主體為用戶,許可權的內容有多種,關係為M - M,具體為:
用戶模型:

public class User
{
    public int UserId{ get; set; }
    public string UserName { get; set; }
}

用戶表Auth_User

欄位 類型 說明
*UserId int 用戶ID
UserName varchar 用戶名
... ... ...

許可權枚舉:

[Flags]
public enum Permission
{
    Add = 1,
    Update = 2,
    Delete = 4,
    Select = 8
}

許可權表 Auth_Permission

欄位 類型 說明
*PermissionId int 許可權ID
Permission varchar 許可權內容

用戶-許可權關係表 Auth_UserPermission

欄位 類型 說明
*UserId int 用戶ID
*PermissionId int 許可權ID

假如一個用戶有增、改兩種許可權,那麼關係表(Auth_UserPermission)可以存儲為:

UserId Permission
1 1
1 2

於是對於許可權的基本操作我們可以進行歸納:

  • 授權:INSERT 許可權表 (用戶ID,許可權的具體值)
  • 校權:EXISTS 許可權表 UserID==用戶ID AND Permission==要判斷許可權的具體值

我們留意到對於Permission的枚舉定義,值使用了對2的冪運算的值:

冪運算 十進位 二進位 十六進位
2^0 1 0001 0x01
2^1 2 0010 0x02
2^2 4 0100 0x04
2^3 8 1000 0x08

這麼定義是有好處的,對於Auth_UserPermission的表存儲可以節省存儲空間,並且程式便於處理,譬如:
如果UserId=1的用戶擁有Add、Select許可權,Auth_UserPermission表原本應該存儲兩條記錄:

  • (1,1)
  • (1,8)

現在,可以考慮更簡單的存儲方式

  • (1,9)

這表示:
Permission.Add | Permission.Select
等價於
1 按位或 8 ( 1 | 8 )
等價於
9

而對於許可權的判斷,則使用存儲的許可權值按位與要進行校權的值是否等於要進行校權的值來判斷
譬如判斷用戶是否擁有Delete許可權,則使用9按位與4是否等於4來進行判斷,用C#的三目運算來表示為:

9 & 4 == 4 ? "有許可權":"無許可權"

這樣被標記有Flags特性的枚舉在.Net框架中遍佈各種基礎類庫,譬如反射中的BindFlags枚舉。本身屬於基礎知識,由於不常應用所以容易被忽視,在許可權中屬於應用小技巧。還有人質疑這麼存儲會有性能問題,在後面章節講到優化時,再行討論。

於是我們對使用了小技巧的新的許可權基本控制再次進行歸納:

  • 授權:INSERT 許可權表(用戶ID,所有擁有許可權的按位或值)
  • 校權:EXISTS 許可權表(UserID == 用戶ID AND Permission & 要判斷許可權的具體值 == 要判斷許可權的具體值)

2. 基於角色的基本許可權控制

隨著業務系統的發展,業務系統有了第一次升級機會,並附帶了一個新的許可權需求:

系統需要滿足一類職位的人擁有相同的許可權

按照第一節的內容,這個需求其實不用做任何變化一樣可以滿足,但是問題在於負責授權的人“太累了”,對於每一個用戶,我們可能都要做一遍授權的操作。
為瞭解決這個問題,我們引入角色這一基本單元,角色是一種抽象,可以具體到業務場景的類似職位、身份等概念。

角色模型設計:

public class Role
{
    public int RoleId { get;set; }
    public string RoleName { get;set; }
}

角色表設計Auth_Role:

欄位 類型 說明
*RoleId int 角色ID
RoleName varchar 角色名稱

基於角色的基本許可權控制的原則是:

  1. 簡化用戶許可權的操作;
  2. 許可權操作的對象從用戶變更為角色
  3. 不能對單一用戶做許可權操作,僅對角色做許可權操作,每個需要許可權的用戶,都擁有至少一個角色;

角色與用戶的抽象關係表現為M-M,這表示:

  • 一個用戶可以擁有多個角色;
  • 一個角色下有多個用戶;

具體到業務可以是一個人可以有多個職位;一個職位下有多個人;
針對此設計,我們需要做以下操作:

  1. 從系統中刪除掉原來的Auth_UserPermission關係;
  2. 新增Auth_UserRole(UserId,RoleId)的關係;
  3. 新增Auth_RolePermission(RoleId,Permission)的關係;

假定業務系統有這樣的職位列表:

RoleId RoleName
1 總裁
2 開發總監

假設用戶ID等於1001的用戶職位為總裁兼開發總監,那麼關係表Auth_UserRole可以存儲為:

UserId RoleId
1001 1
1001 2

業務約定:總裁有增、刪、改、查四個許可權,開發總監則有增、查兩個許可權,那麼關係表Auth_RolePermission可以存儲為:

RoleId Permission
1 15( = 1 | 2 | 4 | 8 )
2 9

我們對給予角色的基本許可權控制操作再次歸納為:

  • 授權:給角色添加許可權(INSERT Auth_RolePermission),給用戶添加角色(INSERT Auth_UserRole)
  • 校權:應當是拿出用戶所有的角色,並再次拿出這些角色的許可權做並集,並DISTINCT 許可權並集為許可權集合,判斷許可權集合是否含有需要校權的許可權

3. 基於角色並含有用戶組概念的許可權控制

春去秋來,業務系統迎來了第二次升級機會,並包含以下新的許可權需求:

所有部門的開發崗位擁有相同的增、查許可權

基於第二節的系統升級,解決此需求我們會有臨時的做法:做一個角色,給所有開發崗的同事賦予這個角色。

這樣的臨時做法的確解決了我們的問題,但這裡有幾個問題,函待解決:

  1. 系統沒有部門的對應抽象;
  2. 一旦其中一個部門的開發崗同事擁有的許可權有變動,我們需要新建角色,並重新授權;

針對此兩個問題,我們引入一個新的模型:用戶組(UserGroup),用戶組的概念在業務系統中,可以具體為:部門、小組、團隊等

用戶組模型設計:

public class UserGroup
{
    public int UserGroupId { get; set; }
    public int ParentId { get;set; } //留意此欄位,將在本節末尾闡述
    public string UserGroupName { get; set; }
}

用戶組表Auth_UserGroup設計:

欄位 類型 說明
*UserGroupId int 部門ID
ParentId int 上級部門ID
UserGroupName varchar 部門名稱

基於角色並含有用戶組概念的許可權控制有以下特點:

  1. 再次簡化了用戶許可權的操作;
  2. 用戶可以擁有角色;用戶組也可以擁有角色;
  3. 許可權的操作對象依舊為角色,不可對用戶、用戶組進行許可權操作;

用戶與用戶組的關係表現為多對多,這表示一個用戶可以屬於多個用戶組,一個用戶組下可以有多個用戶,具體到業務可以描述為:一個人可以在多個部門,一個部門下可以有多個人;

用戶組與角色的關係表現為多對多,這表示一個用戶組的所有用戶可以擁有相同的多個角色,一個角色下有多個用戶組,具體到業務可以描述為:同一個部門的人可以擁有多個相同的職位;

為了實現此設計,我們需要做以下新的操作:

  1. 新增Auth_UserUserGroup關係;
  2. 新增Auth_UserGroupRole關係;

假設系統擁有這樣的部門列表:

UserGroupId UserGroupName
1 總裁辦
2 前端開發部
3 中台開發部
4 人力資源部
5 保全部

假設用戶ID為1101的用戶既是前端開發部的開發總監,又是中台開發部的開發總監;中台開發部、前端開發部的所有同事本質都是開發,且所有開發部的同事都有增、查的許可權,那麼:

用戶-用戶組Auth_UserUserGroup關係表可以存儲為:

UserId UserGroupId
1101 2
1101 3

新增角色:開發

RoleId RoleName
6 開發

Auth_RolePermission新增記錄:

RoleId Permission
6 9

Auth_UserGroupRole關係表可以存儲為:

UserGroupId RoleId
2 6
3 6

這樣,我們就滿足了本節提出的需求。
另外要註意到的是用戶組的ParentId欄位,不要輕視這個簡單的樹狀設計,實際應用中根據業務場景會有各種不同的問題,譬如不良的SQL導致DB層面做了遞歸查詢、上級部門許可權與下級部門許可權的繼承關係,但這本質屬於業務需求,不再贅述

4. RBAC許可權模型

現在,系統經過3次升級,已經有了較為完備的許可權體系,我們解決了大部分問題。
但是我們也註意到,所有的有關於許可權的定義僅僅圍繞著增刪改查這一類許可權控制。假如系統現在需要多控制一部分許可權內容,我們就有些捉襟見肘了。

簡單來說,我們的許可權模型設計對於擴展支持不夠

譬如,業務系統初期對系統的菜單可見性有許可權控制,隨著系統迭代,可能出現對文件的可操作性也需要有許可權控制,這是很正常的事,顯然,依照我們的設計,系統無法滿足。

回顧1、2、3節的升級內容,我們的問題其實是由單一許可權元素變更為多元許可權元素,如果我們能重新將被控制元素變更為單一元素,我們之前的設計則不用變更。

為瞭解決這個問題,我們對各種許可權元素進行抽象,譬如文件訪問許可權和菜單訪問許可權。抽象為如下圖內容:

現在,許可權的Root節點變成了Permission這個抽象,它沒有具體的意義,但他將各類許可權集中在了一起,使得多種許可權元素重新集中在單一Permission這個抽象元素上,再次揉入到我們的系統中,如下圖:

這就是許可權系統的RBAC完成模型。

至此,藉助RBAC模型,我們完成了許可權模塊的理論設計,它能滿足大量許可權控制場景,也是業界慣用的手段,RBAC模型是一種許可權模型的總結和歸納,市面上能見到的各種許可權控制,都與RBAC沾邊,也就是說,掌握RBAC,就掌握了閱讀各種系統許可權設計的基礎,有了理論支持。

不過值得註意的是,雖然我們有了理論基礎,但實際應用中,我們還要做一些擴展內容。

譬如說許可權歷史,許可權模塊屬於敏感內容,是系統的中樞所在,嚴謹的許可權模塊肯定是不會對操作進行Delete的,而是Fake Delete以保留歷史。上文中這樣的設計為此提供了方便,當用戶的許可權發生變更時,我們只需要對關係做Fake Delete即可。當然,關係本身需具備IsFakeDeleted屬性。

下一章節將介紹dotnet core的具體實現。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 開發過程中,我們經常會碰到這樣的需求:在柱狀圖上,點擊某條柱形,調用相應的方法或跳轉相應的界面 接下來就詳細介紹如何實現柱狀圖的點擊事件,其中maChart是繪圖對象 一、簡單的點擊事件 這樣就可以獲得每條柱形所對應的數據 若需要在每條柱形上添加額外的屬性,比如id等等,可以在series中,通過對 ...
  • 效果圖: ...
  • JS中有關引用對象的拷貝問題 問題描述:在開發過程中,拷貝一個對象數組給另一個數組的時候,改變新數組中對象的屬性值,原數組中的對象屬性值也跟著改變了。 例如新定義一個數組arr1,裡面有兩個對象,然後再新定義一個數組arr2,將arr1的值通過slice()方法拷貝給arr2,由於slice()方法 ...
  • 1、斷開原因 WebSocket斷開的原因有很多,最好在WebSocket斷開時,將錯誤列印出來。 錯誤狀態碼: WebSocket斷開時,會觸發CloseEvent, CloseEvent會在連接關閉時發送給使用 WebSockets 的客戶端. 它在 WebSocket 對象的 onclose ...
  • 待補充 ...
  • 領域建模有很多種方法,對於同樣的問題域使用不同的建模手段得到的模型可能也不盡相同。於是我經常聽到這樣一個問題:怎麼才能保證建模的正確性? 領域建模有很多種方法,對於同樣的問題域使用不同的建模手段得到的模型可能也不盡相同。於是我經常聽到這樣一個問題:怎麼才能保證建模的正確性? 這聽起來是個合理的質疑, ...
  • 1.安裝Tomcat並配置啟動 2.Tomcat部署項目 網市場 1.配置server.xml文件 ,新增在 engline內 2.創建站點目錄,上傳源碼包 3.使用mvn編譯 4.Nginx配置反向代理 5.重啟Tomcat服務 6.配置功能變數名稱解析 7.安裝圖解: 8.部署多節點組建集群 9.接入負 ...
  • (上圖是聖卡塔利娜島,美國南加州的一個小島,也是 mac OS 10.15 版本的官方預設壁紙) ___ 概述 Hello,大家好,我們又來講面試中的基礎題了,今天這是一道很經典又很猥瑣的題 說猥瑣是因為這兩個異常名字比較近似,但事實上他們完全不同,導致很多同學會經常容易把它們搞混 說經典是因為由這 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...