Yii 1.x 內置 CAuthManager (CDbAuthManager) 提供 RBAC 基礎,但缺乏圖形化管理界面、自動路由映射、多級繼承可視化。srbac 是社區最成熟的 RBAC 管理模塊,提供 Web 界面管理角色/任務/操作、自動掃描控制器動作生成操作項、支持業務規則。本文深度解析其數據表設計、核心驗證邏輯、控制器集成方式,幫助開發者理解底層原理並進行二次開發。
srbac 通過配置映射三張核心表(可自定義表名):
| 邏輯表 | 默認表名 | 關鍵字段 | 說明 |
|---|---|---|---|
| 授權項目表 | AuthItem | name(PK), type(0操作/1任務/2角色), description, bizrule, data | 存儲所有角色、任務、操作節點 |
| 項目關係表 | AuthItemChild | parent, child (雙PK) | 父子繼承關係:角色→任務、任務→操作,支持多級 |
| 分配表 | AuthAssignment | itemname, userid (雙PK), bizrule, data | 用戶-角色綁定,bizrule 支持動態判斷 |
'modules' => array(
'srbac' => array(
'itemTable' => 'AuthItem',
'itemChildTable' => 'AuthItemChild',
'assignmentTable' => 'AuthAssignment',
'userClass' => 'User',
'userIdColumn' => 'id',
'userNameColumn' => 'username',
'layout' => 'application.views.layouts.main',
'debug' => false, // 生產環境必須 false
),
),
所有需權限控制的控制器繼承 SBaseController(而非 Controller):
class SBaseController extends Controller {
protected function beforeAction($action) {
// 1. 構建訪問標識:模塊.控制器.動作 (首字母大寫)
$del = Yii::app()->getModule('srbac')->delimiter; // 默認 '.'
$mod = $this->module ? $this->module->id . $del : '';
$controller = implode('.', array_map('ucfirst', explode('/', $this->id)));
$access = $mod . $controller . ucfirst($this->action->id); // 如: admin.UserController.View
// 2. 白名單檢查
if (in_array($access, $this->allowedAccess())) return true;
// 3. srbac 未安裝/調試模式放行
$srbac = Yii::app()->getModule('srbac');
if (!$srbac->isInstalled() || $srbac->debug) return true;
// 4. 核心權限驗證
if (!Yii::app()->user->checkAccess($access) || Yii::app()->user->isGuest) {
$this->onUnauthorizedAccess(); // 默認導向登錄/403
return false;
}
return true;
}
// 可覆寫:白名單(如登錄、錯誤、公共頁)
public function allowedAccess() {
return array(
'SiteController.Login', 'SiteController.Logout', 'SiteController.Error',
'SiteController.Captcha', 'SrbacController.*', // srbac 管理界面放行
);
}
// 可覆寫:無權限處理
protected function onUnauthorizedAccess() {
if (Yii::app()->request->isAjaxRequest) {
echo CJSON::encode(array('success' => false, 'msg' => '權限不足'));
Yii::app()->end();
} else {
$this->redirect(Yii::app()->user->loginUrl);
}
}
}
Yii::app()->user->checkAccess($access) 最終調用 CDbAuthManager::checkAccess(),流程如下:
// CDbAuthManager::getAuthAssignments($userId)
$rows = $this->db->createCommand()
->select()->from($this->assignmentTable)
->where('userid=:userid', array(':userid'=>$userId))
->queryAll();
foreach ($rows as $row) {
$assignments[$row['itemname']] = new CAuthAssignment($this, $row['itemname'], $row['userid'], $row['bizrule'], @unserialize($row['data']));
}
protected function checkAccessRecursive($itemName, $userId, $params, $assignments) {
// 1. 獲取授權項目定義
if (($item = $this->getAuthItem($itemName)) === null) return false;
// 2. 執行項目級 bizrule
if (!$this->executeBizRule($item->bizRule, $params, $item->data)) return false;
// 3. 默認角色直接通過
if (in_array($itemName, $this->defaultRoles)) return true;
// 4. 直接分配檢查
if (isset($assignments[$itemName])) {
$assignment = $assignments[$itemName];
if ($this->executeBizRule($assignment->bizRule, $params, $assignment->data)) return true;
}
// 5. 遞歸查找父角色(關鍵:支持多級繼承)
$parents = $this->db->createCommand()
->select('parent')->from($this->itemChildTable)
->where('child=:name', array(':name'=>$itemName))
->queryColumn();
foreach ($parents as $parent) {
if ($this->checkAccessRecursive($parent, $userId, $params, $assignments)) return true;
}
return false;
}
public function executeBizRule($bizRule, $params, $data) {
return $bizRule === '' || $bizRule === null || ($this->showErrors ? eval($bizRule) != 0 : @eval($bizRule) != 0);
}
// 典型 bizrule 示例:return Yii::app()->user->id == $params['post']->author_id; // 僅作者可編輯
| 特性 | 實現方式 | 擴展建議 |
|---|---|---|
| 自動掃描控制器動作 | SrbacModule::install() 遍歷 controllers 目錄,解析 action* 方法生成 AuthItem (type=0) | 可擴展掃描註解 @permission 自定義分組/描述 |
| 多級角色繼承 | AuthItemChild 父子關係 + 遞歸查找父節點 | 注意避免循環繼承,安裝時檢測 |
| 動態權限 (bizrule) | PHP 代碼字符串存儲,運行時 eval() 執行 | 安全風險大,建議改用 PHP 7.4+ 箭頭函數序列化或策略類替代 |
| Ajax/RESTful 支持 | onUnauthorizedAccess() 區分請求類型返回 JSON/重定向 | 統一 API 錯誤碼 403 + 結構化響應 |
| 管理界面 | SrbacController 提供角色/任務/操作 CRUD、矩陣分配、圖形化繼承圖 | 可二次開發添加權限變更審計日志 |
debug=false、allowedAccess() 包含 SrbacController.*、用戶有 srbac 角色。ActionIndex (首字母大寫),驗證時 ucfirst($action->id) 必須一致。delimiter 默認 .,若控制器 ID 含 / (如 admin/user),生成 admin.UserController.Action,需與 AuthItem 名稱完全一致。bizrule 字符串表達式(如 return $params['userId']==$data['ownerId'];)AuthManager::checkAccess 覆寫或策略類,棄用 eval。allowedAccess() 或 checkAccessRecursive 入口判斷 Yii::app()->user->isSuperAdmin 直接返回 true。srbac 通過三表 RBAC 模型 + 控制器基類攔截 + 遞歸權限解析 + Web 管理界面,為 Yii 1.x 提供了開箱即用的完整權限管理解決方案。理解其數據表關係、checkAccess 遞歸邏輯、bizrule 執行機制、控制器集成點,能讓你在權限異常排查、性能優化、功能擴展(如多租戶、動態權限、API Token 鑑權)時得心應手。建議新項目考慮遷移至 Yii 2.x yii2-rbac 或 mdmsoft/yii2-admin,享受更現代的架構與類型安全。