[七年技術總結系列][理論篇]-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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...