寫程序,做產品,過日子

          成功其實很簡單,就是強迫自己堅持下去

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            69 Posts :: 1 Stories :: 92 Comments :: 0 Trackbacks

          RBAC(基于角色的權限控制)是一個老話題了,但是這兩天我試圖設計一套表結構實現完整的RBAC時,發現存在很多困難。

          我說的完整的RBAC,是指支持角色樹形結構和角色分組。具體來說,應當包含如下權限控制需求:

          1. 父級角色可以訪問甚至是修改其子級的數據,包含直接子級直到最終子級。
          2. 角色可以訪問其所在組的數據。
          3. 父級角色可以訪問其所有子級(從直接子級到最終子級)所在組的數據。

          而具體到我的系統中,還應當有如下需求。

          1. 兼容多種數據庫產品。只能用簡單的表,視圖,存儲過程和函數等實現。
          2. 同時兼容單條數據處理和批量數據處理的需求。

          且不論這些具體需求,RBAC的基本表應當如下四個:

          • roleList表,記錄所有的角色和角色組。
            • roleId: PK, 角色/組的ID,全局唯一,不區分角色和組。
            • roleName:角色/組的名稱。
            • roleType: R - 角色,G - 組
          • rolePermission表,記錄每一個角色/組對每一個對象的權限。
            • permissionID: PK, 無特定意義。
            • role: 角色/組的ID。
            • object: 對象的ID。
            • permission: 權限標識,如讀,寫,刪等。
          • roleRelationship表,記錄角色/組之間的關系。
            • relationId: PK, 無特定意義。
            • superiorRole: 父角色/組的ID。
            • role:子角色,子組,成員角色,成員組的ID。
            • relationship: 關系標識,可在如下設置集中選取一個。
              • PG標識:P - 父子關系,G - 組/成員關系。
              • PPGG標識:在PG集上,再加三種:PP - 間接父級關系,GG - 組內組關系,CG - parentRole是組,childRole的子角色或間接子角色是其成員,或其子組(含間接子組)的成員
          • objectList表,記錄所有的對象。
            • objectId: PK,對象ID,全局唯一。
            • objectName: 對象名稱。
            • ... ...

          分析上述表結構,不難發現,問題的關鍵在于從rolePermission表中讀取數據時,如何限定角色/組的范圍.

          方案一

          如果角色和組的總量不大,比如在100以內,采用PPGG標識關系,讀取數據時是最快的。這個時候的SQL只需要一個輸入參數?roleId:

          SELECT object FROM rolePermission p left join roleRelationship r on p.role = r.role WHERE p.role = ?roleId or r.superiorRole = ?roleId. (尚未驗證SQL的正確性)

          但是,這個方案是以極度冗余roleRelationship表的數據為代價的,比如有100個角色,那么roleRelationship中將會有100 * 100 =10,000條記錄。而在每次調整角色和R角色組的時候,就要在roleRelationship中一次增加或刪除100條記錄。這個開銷是比較大的。

          方案二

          只標識PG,查詢時接收的輸入參數為一個完整的相關角色列表?roleList。

          SELECT object FROM rolePermission WHERE role in (?roleList)

          在系統運行時,這個?roleList通常可以從role hierarchy cache中取到,比較方便。這個方案的主要問題有二:

          1)如果?roleList過長,使用in判斷性能會很差。

          2)在有些情況下,如報表查詢和系統外查詢時,取得roleList不太方便。

          方案三

          只標識PG,但使用如下三個數據庫函數來判斷角色/組之間的關系。

          • boolean isChild(role, parentRole) - 如role為parentRole的子,返回true。
          • boolean isDescendant(role, ancestorRole) - 如role為ancestorRole的子或間接子級,返回true。
          • boolean isMember(role, group) - 如role為group的成員或子組的成員,返回true。
          • boolean descendantIsMember(role, group) - 如role的子或間接子級為group的成員,返回true。
          • boolean isBelong(role, super) - 如role為super的子,間接子,成員或間接員,或者role的子(含間接子)是super的成員或子組成員,返回true。

          在查詢時,也只需要接收一個?roleId:SELECT object FROM rolePemission WHERE isBelong(?roleId, role)

          如何寫出高性能的數據庫函數是實現這個方法的關鍵。

          上述方法僅是理論分析,我傾向于方案二。

          終于想到新的方案了。

          方案四,

          結合方案一和方案二,在roleRelationship中,對前兩級(也可以是三級或四級)角色,保存其所有的下級角色和組。這樣,如果以前兩級角色查詢數據,就使用方案一,如果以第三級及以下的角色查詢數據,就使用方案二。

          仍以100個角色為例,每個角色要保存三個關系:一級主管角色,二級主管角色,直接主管角色,最多有300條數據。

          每往角色組中加一個角色,也需要加入三條數據:角色本身,一級主管角色,二級主管角色。

          但往角色組中加一個子組,需要加入的數據量就大一些:子組本身,子組所有角色,子組所有角色的一級主管角色和二級主管角色。如在多個子組中發現同一角色,可重復保存,但應在表中附加說明是由哪個子組導入的。這樣在刪除子組時就可以有選擇性的刪除。

          但重復子組的情況就比較麻煩,還有等考慮。假充有組g01,g11,g12,g21。g01包含g11和g12,g11和g12分別包含g21。從g01中刪除g11時,如何判斷g21的去留?看來還是應當在維護時判斷應不應當刪除。

          Technorati :

          posted on 2006-12-27 17:12 Welkin Hu 閱讀(6159) 評論(0)  編輯  收藏 所屬分類: MicrosoftJava
          主站蜘蛛池模板: 金湖县| 陇南市| 玛多县| 开封县| 海盐县| 汉阴县| 崇文区| 南华县| 达拉特旗| 唐山市| 南安市| 保康县| 武定县| 前郭尔| 凉山| 大城县| 北安市| 当涂县| 新平| 临洮县| 兰考县| 海安县| 木里| 宁明县| 神农架林区| 上栗县| 大关县| 尉氏县| 米林县| 九江县| 祥云县| 邳州市| 平武县| 黄浦区| 安顺市| 元阳县| 阜城县| 江陵县| 灌阳县| 措勤县| 白朗县|