關於權限設計的探討(轉貼)

        但凡涉及多用戶不同權限的網絡或者單機程序,都會有權限管理的問題,比較突出的是MIS系統。 
下面我要說的是MIS系統權限管理的數據庫設計及實現,當然,這些思路也可以推廣開來應用,比如說在BBS中用來管理不同級別的用戶權限。 
權限設計通常包括數據庫設計、應用程序接口(API)設計、程序實現三個部分。 
這三個部分相互依存,密不可分,要實現完善的權限管理體系,必須考慮到每一個環節可行性與複雜程度甚至執行效率。 
我們將權限分類,首先是針對數據存取的權限,通常有錄入、瀏覽、修改、刪除四種,其次是功能,它可以包括例如統計等所有非直接數據存取操作,另外,我們還可能對一些關鍵數據表某些字段的存取進行限制。除此,我想不出還有另外種類的權限類別。 
完善的權限設計應該具有充分的可擴展性,也就是說,系統增加了新的其它功能不應該對整個權限管理體系帶來較大的變化,要達到這個目的,首先是數據庫設計合理,其次是應用程序接口規範。 
我們先討論數據庫設計。通常我們使用關係數據庫,這裏不討論基於Lotus產品的權限管理。 
權限表及相關內容大體可以用六個表來描述,如下: 
1 角色(即用戶組)表:包括三個字段,ID,角色名,對該角色的描述; 
2 用戶表:包括三個或以上字段,ID,用戶名,對該用戶的描述,其它(如地址、電話等信息); 
3 角色-用戶對應表:該表記錄用戶與角色之間的對應關係,一個用戶可以隸屬於多個角色,一個角色組也可擁有多個用戶。包括三個字段,ID,角色ID,用戶ID; 
4 限制內容列表:該表記錄所有需要加以權限區分限制的數據表、功能和字段等內容及其描述,包括三個字段,ID,名稱,描述; 
5 權限列表:該表記錄所有要加以控制的權限,如錄入、修改、刪除、執行等,也包括三個字段,ID,名稱,描述; 
6  權限-角色-用戶對應表:一般情況下,我們對角色/用戶所擁有的權限做如下規定,角色擁有明令允許的權限,其它一律禁止,用戶繼承所屬角色的全部權限,在  此範圍內的權限除明令禁止外全部允許,範圍外權限除明令允許外全部禁止。該表的設計是權限管理的重點,設計的思路也很多,可以說各有千秋,不能生搬硬套 說 某種方法好。對此,我的看法是就個人情況,找自己覺得合適能解決問題的用。 
先說第一種也是最容易理解的方法,設計五個字段:ID,限制內容ID,權限ID,角色/用戶類型(布爾型字段,用來描述一條記錄記錄的是角色權限還是用戶權限),角色/用戶ID,權限類型(布爾型字段,用來描述一條記錄表示允許還是禁止) 
好了,有這六個表,根據表六,我們就可以知道某個角色/用戶到底擁有/禁止某種權限。 
或 者說,這麼設計已經足夠了,我們完全實現了所需要的功能:可以對角色和用戶分別進行權限定製,也具有相當的可擴展性,比如說增加了新功能,我們只 需要添 加一條或者幾條記錄就可以,同時應用程序接口也無須改動,具有相當的可行性。但是,在程序實現的過程中,我們發現,使用這種方法並不是十分科學,例 如瀏 覽某個用戶所擁有的權限時,需要對數據庫進行多次(甚至是遞歸)查詢,極不方便。於是我們需要想其它的辦法。使用過Unix系統的人們都知道, Unix 文件系統將對文件的操作權限分爲三種:讀、寫和執行,分別用1、2、4三個代碼標識,對用戶同時具有讀寫權限的文件被記錄爲3,即1+2。我們也 可以用 類似的辦法來解決這個問題。初步的想法是修改權限列表,加入一個字段:標識碼,例如,我們可以將錄入權限標識爲1,瀏覽權限標識爲2,修改權限標識 爲 4,刪除權限標識爲8,執行權限標識爲16,這樣,我們通過權限累加的辦法就可以輕易的將原本要分爲幾條記錄描述的權限放在一起了,例如,假定某用戶  ID爲1,庫存表對應的限制內容ID爲2,同時規定角色類型爲0、用戶類型爲1,我們就可以將該用戶具有錄入、瀏覽、修改、刪除庫存表的權限描述爲:  2,15,1,1。 
確實很簡單,不是嗎?甚至還有更過激的辦法,將限制內容列表也加上一列,定義好標識碼,這樣,我們甚至可以用簡單的一條記 錄描述某個用戶具有的對全 部內容所具有的全部權限了。當然,這樣做的前提是限制內容數量比較小,不然,呵呵,2的n次方遞增起來可是數量驚人,不容易解 析的。 
從表面上看,上述方法足以達到實現功能、簡化數據庫設計及實現的複雜度這個目的,但這樣做有個弊端,我們所涉及的權限列表不是相互獨立而 是互相依賴 的,比如說修改權限,其實是包含瀏覽權限的,例如,我們可能只是簡單的設置用戶對庫存表存取的權限值爲錄入+修改+刪除(1+4+8= 13),但事實上, 該用戶具有(1+2+4+8=15)的權限,也就是說,在這種方案中,13=15。於是當我們調用API詢問某用戶是否具有瀏覽權限 時,就必須判斷該用戶 是否具有對該數據表的修改權限,因此,如果不能在程序中固化權限之間的包含關係,就不能利用應用程序接口簡單的做出判斷。但這與我 們的目的“充分的可擴展 性”矛盾。 
這個問題如何解決?我想到了另外一種設置標識碼的方法,那就是利用素數。我們不妨將錄入、瀏覽、修改、刪 除、執行的基本標誌碼定爲 2,3,5,7,11,當遇到權限互相包含的時候,我們將它的標識碼設定爲兩個(或多個)基本標誌碼的乘積,例如,可以將“修 改”功能的標誌碼定爲3*5 =15,然後將所有的權限相乘,就得到了我們需要的最終權限標識值。這樣,我們在詢問用戶是否具有某項權限的時候,只需要將 最終的值分解成質因子,例如, 我們可以定義一個用戶具有錄入+修改+刪除庫存表的權限爲 2*15*7=2*3*5*7,即表示,該用戶具有了對庫存表 錄入+瀏覽+修改+刪除權限。 
當然,對權限列表我們使用上述方法的前提是權限列表記錄條數不會太多並且關係不是十分複雜,否則,光是解析權限代碼就要機器忽悠半宿:) 
我 希望以上的分析是正確且有效的(事實上,我也用這些的方法在不止一套系統中實現),但無論如何,我覺得如此實現權限管理,只是考慮了數據庫設計和 應用程 序接口兩部分內容,對於實現,還是顯得很費勁。因此,我懇請有過類似設計、實現經驗的同志們提出建設性的意見和修改建議。 
另外,關於數據庫設計的思路還有使用二維表的,這將在以後的時間裏討論,關於應用程序接口的設計和實現我也將在利用另外篇幅和大家共同探討,代碼將用類C語法實現(我不喜歡pascal,抱歉) 
歡迎朋友們和我聯繫,mailto:[email protected],也歡迎訪問我和另外一位朋友共同建設的網站:http://www.91search.com,那裏將有一個音樂搜索的工具軟件提供下載。
 
========================================
關於權限包容關係通過角色和權限掩碼來實現。
 /// <summary>
 /// 權限保護類型枚舉類型。
 /// </summary>
 public enum ProtectEnum
 {
  /// <summary>撤回權限保護類型</summary>
  RevokeProtect = 0,
  /// <summary>授予權限保護類型</summary>
  GrantProtect = 1,
  /// <summary>拒絕權限保護類型</summary>
  DenyProtect  = 2
 }
 /// <summary>
 /// 系統固定用戶或角色枚舉類型。
 /// </summary>
 /// <remarks>
 /// 管理員角色:16399 = 100000000001111
 /// 所有者角色:16385 = 100000000000001
 /// 只讀者角色:16386 = 100000000000010
 /// 安全員角色:16388 = 100000000000100
 /// 配置員角色:16392 = 100000000001000
 /// </remarks>
 public enum FixedRoleEnum
 {
  ///<summary>系統管理員固定用戶</summary>
  Administrator = 1,
  ///<summary>系統管理員固定角色</summary>
  Administrators = 16399,
  ///<summary>所有者固定角色(具有讀寫操作之權限)</summary>
  Authors  = 16385,
  ///<summary>只讀者固定角色(具有隻讀操作之權限)</summary>
  Readers  = 16386,
  ///<summary>系統安全管理員固定角色</summary>
  Security = 16388,
  ///<summary>系統設置管理員固定角色</summary>
  Setting  = 16392
 }
 /// <summary>
 /// 系統權限枚舉類型。
 /// </summary>
 public enum PermissionEnum
 {
  /// <summary>“讀取”權限</summary>
  FetchPermission = 1,
  /// <summary>“新增”權限</summary>
  AddNewPermission = 2,
  /// <summary>“更新”權限</summary>
  UpdatePermission = 4,
  /// <summary>“刪除”權限</summary>
  DeletePermission = 8,
  /// <summary>“打印”權限</summary>
  PrintPermission = 16,
  /// <summary>系統保留,應用於流程處理</summary>
  FlowPermission = 1024,
  /// <summary>系統保留,應用於流程處理</summary>
  VoidPermission = 2048
 }
如果用戶“Popeye”對“銷售出倉單[2009]”系統對象具有讀寫(讀取+修改+刪除+新增)權限:(權限表定義如下TPermission)
FormID       UID           Permission
=======      ====          ==========
2009         Popeye        1+2+4+8=15
*****  上面系統定義的默認權限肯定是不夠系統使用的,那麼還有一些權限(例如:報關係統中的“計算差異表”“製造申報單”等權限,就由系統再定義),其實不用太  擔心會不夠用的,因爲在一個Form中不可能會出現所有權限情況,所以,系統自定義的權限掩碼可重複使用在不同的表單中。*****
??建議不 要把角色和用戶分開兩張表來存儲(可參考MS-SQL Server中的sys_users表),因爲在後面的權限定義表需要引用這個表的UID(其可爲 用戶或角色,SQL中是使用UID的數值範圍來區別用戶 與角色的,建議也如此。),版主說的角色與用戶分開對待權限設置,這點我不贊成。因爲角色只不過 是一種用戶組,其應該享用用戶的權限定義範圍,在其下屬的 角色成員(注意角色成員不同於用戶或角色哦,其可以爲角色也可以爲用戶)均默認繼承其定義的權 限,除非角色成員重新指派其上級角色定義的權限字。下面給出 我的相關表定義:
TUser(用戶或角色表)
===================
(PK)UID   int              NOT NULL(主鍵)
Name      nvarchar(50)     NOT NULL(唯一性約束)
FullName  nvarchar(100)    NULL
Description   nvarchar(255)  NULL
MasterNo  varchar(25)     NULL(注:該字段對應爲員工表中的員工編號,通過該字段就可以關聯知道該用戶或角色所屬的員工了,在企業管理系統中很有用啊!)
TMember(用戶與角色關係表)
=========================
(*PK)RoleID    int   NOT NULL
(*PK)UserID    int   NOT NULL
TPermission(用戶權限表)
=======================
(*PK)FormID    int   NOT NULL(表示系統中各個模塊或表單的唯一編號)
(*PK)UserID    int   NOT NULL(用戶或角色編號)
Protect        bit   NOT NULL(1:表示顯示授予權限;0:表示顯示拒絕權限)
Permission     int   NOT NULL(權限掩碼和)
***** 如果哪位兄弟有意研究權限與流程定製方面的東東,相信找偶是沒錯的了!!!呵呵~~~    老闆,給分啊~~~~~×××××
 
==========================================
以上的方法與我做的項目的方法基本一致,現摘一部分的表結構,以供大家參考
create table t_workelement /*工作元素表*/
(
 code varchar(20) not null, /*元素的代碼,唯一*/
 name varchar(50)  not null UNIQUE,/*元素的名稱,唯一*/
 type int  not null, /*類型 0-單據操作 1-報表操作 2-功能操作*/
 bcode varchar(20) null,  /*對應操作的單據/報表/功能的代碼*/
 style int  null,  /*單據:類型 0-查看 1-新增 2-修改 3-刪除*/
      /*報表:無*/
      /*功能:無*/
 term ntext  null,  /*單據:查看/修改/刪除時要符合的條件,如"{$承攬合同.編號}=12/n{$承攬合同.名稱}<>'afd'"*/
 primary key(code)
)
go
drop table t_role
go
create table t_role /*角色表*/
(
 name varchar(30) not null,
 category varchar(50) null,
 remark varchar(100) null,
 primary key(name)
)
go
drop table t_roleelement
go
create table t_roleelement /*角色元素操作表*/
(
 rname varchar(30) not null, /*角色名稱*/
 ecode varchar(20) not null, /*元素的代碼*/
 primary key(rname,ecode)
)
go
drop table t_users
go
create table t_users /*用戶表*/
(
 name varchar(20) not null, /*用戶的名稱*/
 dcode varchar(20) not null, /*所屬的部門*/
 category varchar(50) null,  /*用戶的類別*/
 pswd varchar(15) null,  /*密碼*/
 primary key(name)
)
go
/*插入系統管理員*/
INSERT INTO t_users
(
 name,
 dcode,
 category,
 pswd
)
VALUES
(
 'Admini',
 'system',
 '超級用戶',
 ''
)
go
drop table t_userrole
go
create table t_userrole /*用戶角色表*/
(
 uname varchar(20) not null, /*用戶名稱*/
 rname varchar(30) not null, /*角色的名稱*/
 primary key(uname,rname)
)
go
INSERT INTO t_userrole
(
 uname,
 rname
)
VALUES
(
 'Admini',
 '系統管理員'
)
go
drop table t_dept
go
create table t_dept /*部門表*/
(
 code varchar(20) not null, /*部門的代碼*/
 name varchar(50) not null UNIQUE,/*部門的名稱*/
 type varchar(10) null,  /*部門的類別 行政 倉庫 車間*/
 subtype varchar(16) null,  /*子類別 成品倉庫 原料倉庫自定義*/
 primary key(code)
)
go
/*插入系統管理部*/
INSERT INTO t_dept
(
 code,
 name,
 type
)
VALUES
(
 'system',
 '系統管理部',
 '行政'
)
go
續:關於權限系統的設計
jackyz  Dec 7, 2002 1:00 AM 
source:http://www.jdon.com/jive/article.jsp?forum=46&thread=4110&message=438816
Note:
首 先,向版主致歉。這原是關於權限系統設計問題的回帖,本不應該另開新貼。而在下的另開新貼,一方面是因爲本人的觀點中與很多人 的觀點差別較大,另一方面 也擔心其會被“埋沒”在原貼的大量回帖中。此外,本貼的內容整理與編排耗費了我週末接近一整個晚上的時間,包含了我對近期項目中 權限系統的思考與總結, 希望引來大家足夠的目光和指導。請版主體諒。


權限系統,其重要性當然不言自明。看見大家的方案,有相當多優秀的創意與經驗,我的項目也有所涉及,以下說明的是我最近在一個企業 EJB 項目中初步實現的方案(以及設計考量),望與諸位共商榷。


前言:

權限往往是一個極其複雜的問題,但也可簡單表述爲這樣的邏輯表達式:判斷“who 對 what(which) 進行 how 的操作”的邏輯表達式是否爲真。

針對不同的應用,需要根據項目的實際情況和具體架構,在維護性、靈活性、完整性等N多個方案之間比較權衡,選擇符合的方案。


目標:

這個權限系統的設計,我主要考慮了這麼幾個目標:

直觀,因爲系統最終會由最終用戶來維護,權限分配的直觀和容易理解,顯得比較重要,系統不辭勞苦的實現了組的繼承,除了功能的必須,更主要的就是因爲它足夠直觀。

簡  單,包括概念數量上的簡單和意義上的簡單還有功能上的簡單。想用一個權限系統解決所有的權限問題是不現實的。設計中將常常變化的“定製”特點比較強的部分  判斷爲業務邏輯,而將常常相同的“通用”特點比較強的部分判斷爲權限邏輯就是基於這樣的思路。此外,同時具備 Role 和 Group 的系統難於理 解,權衡之下,摒棄了 Role 概念。

擴展,我在以前的項目中也實現過基於 Role 概念的權限系統,但效果不太理想。之所以在這裏 摒棄 Role 的概念,另一個原因就是因爲它不易擴展。通常 Role 的設計方式意味着預先已經定好了一組權限,這樣的“預先設計”,常常會鼓勵程序 員 hardcode 這些權限相關的部分,而,如果這麼做的話,當需要重新定義 Role 時,擴展就會變得極爲困難。而採用可繼承的 Group 概 念在支持權限以組方式定義的同時有效避免了重定義時在擴展上的困難。


名詞:

下面兩個名詞極其重要,是整個設計問題邊界定義的關鍵,或許我的理解與通常的理解不同,在此有必要特別澄清。

粗粒度:表示類別級,即,僅考慮對象的類別,不考慮對象的某個特定實例。比方,用戶管理中,創建、刪除,對所有的用戶都一視同仁,並不區分操作的具體對象實例。

細粒度:表示實例級,即,需要考慮具體對象的實例,當然,細粒度是在考慮粗粒度的對象類別之後纔再考慮特定實例。比方,合同管理中,列表、刪除,需要區分該合同實例是否爲當前用戶所創建。


原則:

權  限邏輯配合業務邏輯。即,權限系統以爲業務邏輯提供服務爲目標。純粹紙面意義的極其複雜和精巧的權限系統,這裏不作討論。相當多細粒度的權限問題因其極其  獨特而不具通用意義,它們也能被理解爲是“業務邏輯”的一部分。比方,要求:“合同資源只能被它的創建者刪除,與創建者同組的用戶可以修改,所有的用戶 能 夠瀏覽”。這既可以認爲是一個細粒度的權限問題,也可以認爲是一個業務邏輯問題。在這裏我認爲它是業務邏輯問題,在整個權限系統的架構設計之中不予過 多考 慮。當然,權限系統的架構也必須要能支持這樣的控制判斷。或者說,系統提供足夠多但不是完全的控制能力。即,設計原則歸結爲:“系統只提供粗粒度的 權限, 細粒度的權限被認爲是業務邏輯的職責”。

需要再次強調的是,這裏表述的權限系統僅是一個“不完全”的權限系統,即,它不提供所有 關於權限 的問題的解決方法。它提供一個基礎,並解決那些具有“共性”的(或者說粗粒度的)部分。在這個基礎之上,根據“業務邏輯”的獨特權限需求,編碼 實現剩餘部 分(或者說細粒度的)部分,纔算完整。回到權限的問題公式,我的設計僅解決了 who + what + how 的問題,which 的權 限問題留給業務邏輯解決。


概念:

User:用戶。解決 who 的問題。

Group:組。權限分配的單位與載體。權限不考慮分配給特定的用戶。組可以包括組(以實現權限的繼承)。

Operate:操作。表明對 what 的 how 操作。


說明:

User

與大家的都一樣,沒什麼好說的。

Group

與  大家的類似,所不同的是,Group 要實現繼承。即,在創建時必須要指定該 Group 的 Parent 是什麼 Group 。在粗粒度控制上,可 以認爲,只要某用戶直接或者間接的屬於某個 Group 那麼它就具備這個 Group 的所有操作許可。細粒度控制上,在業務邏輯的判斷中,User  僅應關注其直接屬於的 Group ,用來判斷是否“同組”,間接的 Group 對權限的控制意義不大,試設想存在一個 All User 的  Group 是所有 Group 的祖先,這樣的情形下,判斷的結果不具備實際意義。

User 與 Group 是多對多的關係。即,一個 User 可以屬於多個 Group 之中,一個 Group 可以包括多個 User 。

子 Group 與 父 Group 是多對一的關係。即,一個子 Group 只能有一個父 Group ,一個父 Group 可以包括多個子 Group 。


Operate

某種意義上類似於大家的 Resource + Privilege 概念,但,這裏的 Resource 僅包括 Resource Type 不表示 Resource Instance。Operate 概念上與大家的觀點區別比較,後面有詳細的解釋。

Group 與 Operate 是多對多的關係。

各概念的關係圖示如下:

 User
  |*
  |
  |*   1
 Group---+
  |* |*  |
  |  +---+
  |*
 Operate


解釋:

Operate  的定義包括了 Resource Type 和 Method 概念。即, what 和 how 的概念。之所以將 what 和 how 綁定在一起 作爲一個 Operate 概念而不是分開建模再建立關聯,這是因爲很多的 how 對於某 what 纔有意義。比方,發佈操作對新聞對象纔有意義,對 用戶對象則沒有意義。

how 本身的意義也有所不同,這裏並非僅定義類 UNIX 的 RWX 三種操作,這樣的定義對於文件系統是合理 的,但,對於其他的應用領域或許就不是那麼足夠了。具體來說,對於每一個 what 可以定義 N 種操作。比方,對於合同這類對象,可以定義創建操作、 提交操作、檢查衝突操作等。可以認爲,how 概念對應於每一個商業方法。

其中,與 具體用戶身份相關的操作既可以定義在操作的業務邏輯 之中,也可以定義在操作級別。比方,創建者的瀏覽視圖與普通用戶的瀏覽視圖要求內容不同。你既可以在外 部定義兩個操作方法,也可以在一個操作方法的內部 根據具體邏輯進行處理。具體應用哪一種方式應依據實際情況進行處理。

這樣的架構,應能在易於理解和管理的情況下,滿足絕大部分粗粒度權限控制的功能需要。但是,除了粗粒度權限,無可否認,系統中必然還會包括無數對具體 Instance 的細粒度權限。這些問題,被留給業務邏輯來解決,這樣的考慮基於以下兩點。

一  方面,細粒度的權限判斷必須要在資源上建模權限分配的支持信息纔可能得以實現。比方,如果要求創建者和普通用戶看到不同的信息內容,那麼,資源本身應該有  其創建者的信息。如同 Unix 的每一個文件(資源),都定義了對 Owner, Group, All 的不同操作屬性。

另一方 面, 細粒度的權限常常具有相當大的業務邏輯相關性。對不同的業務邏輯,常常意味着完全不同的權限判定原則和策略。相比之下,粗粒度的權限更具通用性,將 其實現 爲一個架構,更有重用價值;而將細粒度的權限判斷實現爲一個架構級別的東西就顯得繁瑣,而且不是那麼的有必要,用定製的代碼來實現就更簡潔,更靈 活。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章