ThinkPHP與RBAC:基於角色的權限管理

訪問控制可以簡單表述爲:判斷誰(Who)對什麼(What/Which)進行怎樣(How)的操作是否爲真。對於一個系統來說,有必要建立一個良好的訪問控制系統,對訪問權限進行合理的分配,用於保證系統的安全性、可靠性。

傳統的訪問控制技術主要有:自主訪問控制(DAC)、強制訪問控制(MAC)和基於角色的權限訪問控制(Role Based Access Control,簡稱RBAC)。

DAC和MAC訪問控制技術均是直接對用戶本身進行權限的管理,細度太小。當用戶數量龐大並且用戶之間關係複雜時,主體和客體關係的匹配及權限的管理就變得複雜起來。並且權限的變更將導致權限分配列表的變更,此時可能會遭遇到很大的困難,甚至於要修改系統的源代碼。

而RBAC訪問控制技術很好地解決了這一問題。在RBAC中,用戶的權限不是在用戶本身上進行管理的,用戶的權限是由用戶所處的角色所決定的。在權限管理中,通過角色這一橋樑將用戶與權限聯繫起來。用戶和角色、角色與權限是一個多對多的關係。

與RBAC訪問控制相關的概念有:

  1. 用戶(User):一個具有唯一標識符的用戶,與權限相分離,只能通過所屬的Role去關聯權限,一個用戶可以擁有多項角色;
  2. 角色(Role):一定數量的權限的集合,角色可以繼承,一個角色對應多項權限;
  3. 權限(Resource):也可以看作是資源,它對應了應用系統中的一個功能;

下面結合一個實例,闡述以上關係。

在這個實例中共有七項權限,分別是:瀏覽用戶、添加用戶、刪除用戶、添加文檔、修改文檔、刪除文檔和審覈文檔。系統中存在着兩個用戶和兩個角色。如下圖所示:

RBAC實例

RBAC實例

由上圖可知:角色A擁有權限1-3,角色C擁有權限4-6,而角色D僅擁有權限7。由於角色B是角色C和角色D的父親角色,則可知角色B擁有角色C和角色D的所有權限,即角色B擁有權限4-7。用戶1分別屬於角色A和角色B,則用戶1擁有權限1-7,而用戶2僅屬於角色D,由此用戶2僅擁有權限7。

按照RBAC的思想,建立訪問控制的數據庫模型。在本模型中通過四張表格來維護訪問控制信息。數據庫模型如下圖所示:

RBAC表模型

RBAC表模型

在ThinkPHP1.5框架中集成了RBAC權限管理,而隨後的ThinkPHP2.0中取消了RBAC,但是可以通過其他方法予以實現。我們先來看ThinkPHP1.5中的RBAC是如何實現的,其流程圖如下:

權限控制流程

權限控制流程

在ThinkPHP1.5中,由App類的init方法執行上述流程,其代碼如下:

  1. if(C('USER_AUTH_ON')){   
  2.     // 啓用權限認證 調用RBAC組件   
  3.     import('ORG.RBAC.RBAC');   
  4.     if(!RBAC::AccessDecision()){   
  5.         // 沒有權限 拋出錯誤   
  6.         if(C('RBAC_ERROR_PAGE')){   
  7.             redirect(C('RBAC_ERROR_PAGE'));   
  8.         }else{   
  9.             throw_exception(L('_VALID_ACCESS_'));   
  10.         }   
  11.     }   
  12. }  
        if(C('USER_AUTH_ON')){
            // 啓用權限認證 調用RBAC組件
            import('ORG.RBAC.RBAC');
            if(!RBAC::AccessDecision()){
                // 沒有權限 拋出錯誤
                if(C('RBAC_ERROR_PAGE')){
                    redirect(C('RBAC_ERROR_PAGE'));
                }else{
                    throw_exception(L('_VALID_ACCESS_'));
                }
            }
        }

在這裏,由RBAC的靜態方法AccessDecision具體負責權限的判斷,每一個權限對應了Module裏的一個action,當用戶所屬的角色授權這項權限時,該用戶即可訪問該方法,否則將轉到認證網關(認證頁面)中。比如用戶A其角色爲Publisher,Publisher擁有Article模塊的index、add、save、edit、update五個action的權限,則用戶A可訪問這五個方法,亦即用戶A能訪問Article/index、Article/add、Article/save、Article/edit、Article/update。

在判斷需要驗證步驟時,顯然需要對哪些模塊哪些操作需要驗證進行設置。相關配置項爲REQUIRE_AUTH_MODULE(需要認證的模塊)、REQUIRE_AUTH_ACTION(需要認證的操作)、NOT_AUTH_MODULE(不需要認證的模塊)、NOT_AUTH_ACTION(不需要認證的操作)。一般情況下,只需要配置其中一項即可,並不需要全部配置。

在取用戶訪問決策列表時,需要從數據庫中查詢用戶所屬角色所擁有的權限。而權限是由模塊和操作決定的。因此需要一張存儲模塊和操作之間的關係的表,一張存儲角色和角色所能訪問的操作關係的表。

考慮到系統的各項菜單和模塊及模塊的操作具有聯繫,如菜單項“用戶管理-添加用戶”必然和用戶管理模塊的添加用戶操作相關,而添加用戶操作由兩步完成:一是提供一個界面供用戶輸入用戶信息,二是處理用戶提交的表單並將信息保存到數據庫中。因此可以再設一張表,將權限和菜單聯繫起來,從而實現自動爲角色配置不同菜單的功能,實現菜單生成的自動化配置。

因此共需五張表,表和所必需的字段如下:

  1. 用戶表(user),字段有id、account、password和role字段;
  2. 角色表(role),字段有id、name;
  3. 節點表(node),字段有id、menu、name、pid和level。level指定了節點所處的層級,層級從1到開始,按照項目、模塊(module)、操作(action)依次爲1、2、3,項目是模塊的父節點,模塊是操作的父節點。menu指定了節點所對應的菜單,且僅需對操作級別的節點設置此值,其他級別的可爲0;
  4. 菜單表(menu),字段有id、pid、name、level、subMenu和menu。如果menu不爲空且level=2,則表明該項是可見於菜單中的。對於level爲1的菜單均以頂層菜單的形式出現;
  5.  可訪問表(access),字段有groupId和userId;

通過以上五張表即可完成基於RBAC的權限控制。和ThinkPHP所要求的表略有出入,少了一個用戶-角色對應表,多了一張菜單表。在ThinkPHP標準中,支持一個用戶對應多個角色,在此處爲了降低複雜度,限制了一個用戶只能對應一個角色。同時增加了菜單表從而實現用戶管理菜單的自動生成。

獲取用戶的訪問決策列表是進行權限驗證的必要步驟。獲取用戶的訪問決策列表的方法是:

  1. 查詢用戶所屬的角色ID;
  2.  根據角色ID從access表中獲取該角色所能訪問的節點列表;
  3.  從node中查詢節點列表的相關信息;
  4.  對產生的節點列表信息進行處理,生成訪問決策列表保存到SESSION中;

最終生成的訪問決策列表的形式(三維數組)是:

  1. $_SESSION['AppName']['ModuleName']['ActionName']  
$_SESSION['AppName']['ModuleName']['ActionName']

AppName是項目名稱,ModuleName是模塊名稱,ActionName是操作名稱。如果在SESSION中存在這樣的一項數據,即表明用戶對指定的“項目-模塊-操作”具有訪問權限,否則就是沒有權限。檢測是否有權限即爲檢測在此數組中是否存在對應項。

在ThinkPHP2.0中去除了RBAC,但是可以另行擴展以實現RBAC。具體可參考示例代碼中的RBAC一項,示例所採用的方法是另行提供一個CommonAction類,在該類的_initialize方法中進行RBAC判斷,即把原Tp1.5中App類的那段權限判斷代碼移到了CommonAction中,此處其他Action再繼續CommonAction,從而實現了RBAC管理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章