訪問控制可以簡單表述爲:判斷誰(Who)對什麼(What/Which)進行怎樣(How)的操作是否爲真。對於一個系統來說,有必要建立一個良好的訪問控制系統,對訪問權限進行合理的分配,用於保證系統的安全性、可靠性。
傳統的訪問控制技術主要有:自主訪問控制(DAC)、強制訪問控制(MAC)和基於角色的權限訪問控制(Role Based Access Control,簡稱RBAC)。
DAC和MAC訪問控制技術均是直接對用戶本身進行權限的管理,細度太小。當用戶數量龐大並且用戶之間關係複雜時,主體和客體關係的匹配及權限的管理就變得複雜起來。並且權限的變更將導致權限分配列表的變更,此時可能會遭遇到很大的困難,甚至於要修改系統的源代碼。
而RBAC訪問控制技術很好地解決了這一問題。在RBAC中,用戶的權限不是在用戶本身上進行管理的,用戶的權限是由用戶所處的角色所決定的。在權限管理中,通過角色這一橋樑將用戶與權限聯繫起來。用戶和角色、角色與權限是一個多對多的關係。
與RBAC訪問控制相關的概念有:
- 用戶(User):一個具有唯一標識符的用戶,與權限相分離,只能通過所屬的Role去關聯權限,一個用戶可以擁有多項角色;
- 角色(Role):一定數量的權限的集合,角色可以繼承,一個角色對應多項權限;
- 權限(Resource):也可以看作是資源,它對應了應用系統中的一個功能;
下面結合一個實例,闡述以上關係。
在這個實例中共有七項權限,分別是:瀏覽用戶、添加用戶、刪除用戶、添加文檔、修改文檔、刪除文檔和審覈文檔。系統中存在着兩個用戶和兩個角色。如下圖所示:
由上圖可知:角色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的思想,建立訪問控制的數據庫模型。在本模型中通過四張表格來維護訪問控制信息。數據庫模型如下圖所示:
在ThinkPHP1.5框架中集成了RBAC權限管理,而隨後的ThinkPHP2.0中取消了RBAC,但是可以通過其他方法予以實現。我們先來看ThinkPHP1.5中的RBAC是如何實現的,其流程圖如下:
在ThinkPHP1.5中,由App類的init方法執行上述流程,其代碼如下:
- 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_'));
- }
- }
- }
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(不需要認證的操作)。一般情況下,只需要配置其中一項即可,並不需要全部配置。
在取用戶訪問決策列表時,需要從數據庫中查詢用戶所屬角色所擁有的權限。而權限是由模塊和操作決定的。因此需要一張存儲模塊和操作之間的關係的表,一張存儲角色和角色所能訪問的操作關係的表。
考慮到系統的各項菜單和模塊及模塊的操作具有聯繫,如菜單項“用戶管理-添加用戶”必然和用戶管理模塊的添加用戶操作相關,而添加用戶操作由兩步完成:一是提供一個界面供用戶輸入用戶信息,二是處理用戶提交的表單並將信息保存到數據庫中。因此可以再設一張表,將權限和菜單聯繫起來,從而實現自動爲角色配置不同菜單的功能,實現菜單生成的自動化配置。
因此共需五張表,表和所必需的字段如下:
- 用戶表(user),字段有id、account、password和role字段;
- 角色表(role),字段有id、name;
- 節點表(node),字段有id、menu、name、pid和level。level指定了節點所處的層級,層級從1到開始,按照項目、模塊(module)、操作(action)依次爲1、2、3,項目是模塊的父節點,模塊是操作的父節點。menu指定了節點所對應的菜單,且僅需對操作級別的節點設置此值,其他級別的可爲0;
- 菜單表(menu),字段有id、pid、name、level、subMenu和menu。如果menu不爲空且level=2,則表明該項是可見於菜單中的。對於level爲1的菜單均以頂層菜單的形式出現;
- 可訪問表(access),字段有groupId和userId;
通過以上五張表即可完成基於RBAC的權限控制。和ThinkPHP所要求的表略有出入,少了一個用戶-角色對應表,多了一張菜單表。在ThinkPHP標準中,支持一個用戶對應多個角色,在此處爲了降低複雜度,限制了一個用戶只能對應一個角色。同時增加了菜單表從而實現用戶管理菜單的自動生成。
獲取用戶的訪問決策列表是進行權限驗證的必要步驟。獲取用戶的訪問決策列表的方法是:
- 查詢用戶所屬的角色ID;
- 根據角色ID從access表中獲取該角色所能訪問的節點列表;
- 從node中查詢節點列表的相關信息;
- 對產生的節點列表信息進行處理,生成訪問決策列表保存到SESSION中;
最終生成的訪問決策列表的形式(三維數組)是:
- $_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管理。