最近有點小忙,好久沒有學習了。。。我懺悔。。。。
RBAC英文全稱Role-Based Access Control。即基於角色的權限控制。它的權限控制原理是將個項目-模塊-操作的使用權限分配給角色組。然后將用戶分到各用戶組中,當然,一個用戶可能有多個用戶組。這樣用戶從屬於用戶組后就具有了該用戶組的相關操作權限。ThinkPHP中有一個封裝的很好的RBAC類庫,十分好用的說。同時官方也提供的demo包中也有rbac的例子,不過,據說好多人看着頭疼。我之前也拿這個例子來看,發現。。。超越了我的理解啊。。。。現在我對rbac的原理大體了解后來看它的demo,我知道問題所在了,它的demo寫的太好。對於我這種剛學習的人而言,絕大部分的精力會不由自主的放進他的操作流程,而會忽略最重要的數據表之間的操作,從而很可能難以理解rbac的原理。
我自己寫了一個rbac的demo,最初的工作是利用curd編寫一個對文章的增改查的程序,連用戶登錄都免了,簡要代碼:
1 class IndexAction extends Action { 2 public function index() {} //文章列表 3 public function add() {} //新增文章 4 public function edit() {} //文章編輯 5 public function view() {} //文章查看 6 }
實現的效果:

一個簡單的系統搭建完成了,下面加入權限控制。
RBAC的實現至少需要五張數據表,(我使用tp默認的前綴think_)分別為:
| 字段 | 類型 | 說明 |
| id | int(11) | pk |
| username | varchar(50) | |
| password | varchar(50) |
| 字段 | 類型 | 說明 |
| role_id | smallint(6) | 角色id |
| node_id | smallint(6) | 節點id |
| level | tinyint | 表示所屬層次,項目=〉1,模塊=〉2,操作=〉3 |
| module | varchar(50) |
| 字段 | 類型 | 說明 |
| id | smallint(6) | pk |
| name | varchar(20) | |
| title | varchar(50) | |
| status | tinyint | |
| remark | varchar(255) | |
| sort | smallint(6) | |
| pid | smallint(6) | |
| level | tinyint |
| 字段 | 類型 | 說明 |
| id | smallint(6) | pk |
| name | varchar(20) | |
| pid | smallint(6) | |
| status | tinyint | |
| remark | varchar(255) |
| 字段 | 類型 | 說明 |
| role_id | smallint(6) | |
| user_id | smallint(6) |
RBAC認證流程圖

rbac的原理其實就是這幾張表的數據邏輯關系。我是手動建立相關數據,一遍數據建立下來,邏輯理清了,基本原理也能理解的不差了。
(一)建立用戶表數據
建立三個用戶,分別為system,admin,user1

(二)建立角色組
建立兩個角色組,分別為admin,user,即管理組和普通組

(三)建立用戶和用戶組的關系
將system,admin划歸角色組1
將user1划歸角色組2

這樣組合用戶的對應關系就建立好了
(4)建立節點表
所謂節點就是所有的項目、模塊、操作的列表。這張表應該算rbac的核心。
我的demo中項目名rabc,有一個模塊即IndexAction,下面還有index,add,edit,view四個操作。將這6條分別計入這張表中,注意pid的設置。同時level的值,1、2、3分別代表項目,模塊,操作。

(5)建立權限表
對於權限的分配就在access表中。
表中數據的意思是role_id=1的組,即admin組具有操作節點1,2,3,4,6的權限,不具備操作節點5即rbac-Index-edit的權限。role_id=2的組即用戶組織具有節點123的操作權限。
很重要的一點就是,權限的安排需要按層次來,即只有具備對項目的操作權限才能操作模塊,同理,只有具備對項目-模塊的操作權限才能操作下面的方法。

這樣,rbac的操作從數據表層面來講已經完成。下面對代碼進行修改以完成rbac的權限控制。
首先,將RBAC.class.php復制到項目目錄Lib\Org下,也可以直接使用系統目錄Lib\ORG\Util下的類庫文件,只要能夠import即可。
然后,在項目配置文件中定義rbac的相關配置項:
//rbac配置項 'USER_AUTH_ON' =>true, 'USER_AUTH_TYPE' =>2, // 默認認證類型 1 登錄認證 2 實時認證 'USER_AUTH_KEY' =>'authId', // 用戶認證SESSION標記 'ADMIN_AUTH_KEY' =>'administrator', 'USER_AUTH_MODEL' =>'User', // 默認驗證數據表模型 'AUTH_PWD_ENCODER' =>'md5', // 用戶認證密碼加密方式 'USER_AUTH_GATEWAY' =>'/Public/login',// 默認認證網關 'NOT_AUTH_MODULE' =>'Public', // 默認無需認證模塊 'REQUIRE_AUTH_MODULE' =>'', // 默認需要認證模塊 'NOT_AUTH_ACTION' =>'', // 默認無需認證操作 'REQUIRE_AUTH_ACTION' =>'', // 默認需要認證操作 'GUEST_AUTH_ON' =>false, // 是否開啟游客授權訪問 'GUEST_AUTH_ID' =>0, // 游客的用戶ID 'SHOW_RUN_TIME' =>true, // 運行時間顯示 'SHOW_ADV_TIME' =>true, // 顯示詳細的運行時間 'SHOW_DB_TIMES' =>true, // 顯示數據庫查詢和寫入次數 'SHOW_CACHE_TIMES' =>true, // 顯示緩存操作次數 'SHOW_USE_MEM' =>true, // 顯示內存開銷 'DB_LIKE_FIELDS' =>'title|remark', 'RBAC_ROLE_TABLE' =>'think_role', 'RBAC_USER_TABLE' =>'think_role_user', 'RBAC_ACCESS_TABLE' =>'think_access', 'RBAC_NODE_TABLE' =>'think_node',
這些配置項可以直接從tp的rbacdemo中復制。每項的意思也基本都注明了。不需要說太多了。
再然后定義一個PublicAction用來放置一些不需要進行認證的模塊。如果修改了配置項中NOT_AUTH_MODULE,那么就建立相應名稱的action。這里面放置登錄,登出,檢驗登錄情況等操作。如果用戶連登錄都發現沒有權限,這是個多么瘋狂的情況,用戶想登錄一直說:你丫無權操作,一邊涼快去。多抓狂。
代碼結構:
class PublicAction extends Action{ // 用戶登錄頁面 public function login() { if (!isset($_SESSION[C('USER_AUTH_KEY')])) { $this->display(); } else { $this->redirect('Index/index'); } } // 登錄檢測 public function checkLogin() { } function loginout() { if (isset($_SESSION[C('USER_AUTH_KEY')])) { unset($_SESSION[C('USER_AUTH_KEY')]); unset($_SESSION); session_destroy(); $this->assign("jumpUrl", __URL__ . '/login/'); $this->success('登出成功!'); } else { $this->error('已經登出!'); } } }
login,logout就是判斷session時候存在。存在就認為已經登錄,執行頁面跳轉或者刪掉這個session以達到退出效果。
checklogin()是rbac的具體實現
public function checkLogin() { if (empty($_POST['username'])) { $this->error('帳號錯誤!'); } elseif (empty($_POST['password'])) { $this->error('密碼必須!'); } //生成認證條件 $map = array(); // 支持使用綁定帳號登錄 $map['username'] = $_POST['username']; import('ORG.Util.RBAC'); $authInfo = RBAC::authenticate($map); //使用用戶名、密碼和狀態的方式進行認證 if (false === $authInfo) { $this->error('帳號不存在或已禁用!'); } else { if ($authInfo['password'] != md5($_POST['password'])) { $this->error('密碼錯誤!'); } $_SESSION[C('USER_AUTH_KEY')] = $authInfo['id']; if ($authInfo['username'] == 'system') { $_SESSION['administrator'] = true; } // 緩存訪問權限 RBAC::saveAccessList(); $this->success('登錄成功!'); } }
首先判斷提交過來的表單信息,然后引入rbac類,然后調用RBAC::authenticate($map);來獲取認證信息。然后根據認證信息來判斷用戶時候登錄成功以及具有的權限。
再然后編寫一個BaseAction.class.php,這里放置一個自動方法,就是每次執行這個action時,這個方法會像構造函數一樣自動執行。這個方法就是檢查用戶權限。
function _initialize() { // 用戶權限檢查 if (C ( 'USER_AUTH_ON' ) && !in_array(MODULE_NAME,explode(',',C('NOT_AUTH_MODULE')))) { import ( '@.Org.RBAC' ); if (! RBAC::AccessDecision ()) { //檢查認證識別號 if (! $_SESSION [C ( 'USER_AUTH_KEY' )]) { //跳轉到認證網關 redirect ( PHP_FILE . C ( 'USER_AUTH_GATEWAY' ) ); } // 沒有權限 拋出錯誤 if (C ( 'RBAC_ERROR_PAGE' )) { // 定義權限錯誤頁面 redirect ( C ( 'RBAC_ERROR_PAGE' ) ); } else { if (C ( 'GUEST_AUTH_ON' )) { $this->assign ( 'jumpUrl', PHP_FILE . C ( 'USER_AUTH_GATEWAY' ) ); } // 提示錯誤信息 $this->error ( L ( '_VALID_ACCESS_' ) ); } } } }
然后修改IndexAction使它由Action改為繼承至BaseAction,這樣每個頁面的執行都會執行自動方法。以后如果增加新的模塊也讓它繼承BaseAction這樣就實現了權限控制了。
以上簡單的權限控制就完成了。代碼基本可以從官方例子中復制稍作修改即可。下面看看效果。
結果不太好表示,按之前對表的編輯,結合實際調試,使用system登錄時,雖然他屬於admin組,而admin組並不具備編輯的權限,但是由於它是系統管理員,判定具有所有權限。用admin登錄可以發現只有編輯鏈接點擊時無權操作。使用user1登錄,除了能看首頁,其他什么事不能干。
就此,RBAC算是結束了。至於官方的demo,是可以再頁面上配置各種值,說白了就是將我的手動編輯表的過程在界面上實現。能夠理解原理,下面就可以試着做出官方demo那樣便捷的操作界面。
附源代碼,希望大神指正。
