Yii2 DbManager 將 RBAC 資料存儲於資料庫四張表(auth_item, auth_item_child, auth_assignment, auth_rule)。本文提供完整後台管理實作:遷移建表、Model 區分角色/權限、Controller CRUD、角色-權限關聯、用戶-角色指派、權限檢查中介層。
# 終端機執行 yii migrate --migrationPath=@yii/rbac/migrations/
生成四表:auth_item (角色+權限), auth_item_child (繼承), auth_assignment (用戶指派), auth_rule (規則)。
<?php
// models/RoleForm.php
namespace app\models;
use Yii;
use yii\rbac\Item;
class RoleForm extends \app\models\AuthItem {
public function init() {
parent::init();
$this->type = Item::TYPE_ROLE; // 常數 1
}
}
// models/PermissionForm.php
namespace app\models;
use Yii;
use yii\rbac\Item;
class PermissionForm extends \app\models\AuthItem {
public function init() {
parent::init();
$this->type = Item::TYPE_PERMISSION; // 常數 2
}
}
// models/AuthItem.php (GII 生成後新增屬性)
namespace app\models;
use Yii;
class AuthItem extends \yii\db\ActiveRecord {
public $child = []; // 用於角色編輯時接收權限陣列
// ... 其他屬性與規則
}
?>
<?php
namespace app\controllers;
use Yii;
use yii\rbac\Permission;
use app\models\PermissionForm;
class PermissionController extends \yii\web\Controller {
public function actionCreate() {
$model = new PermissionForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$perm = new Permission();
$perm->name = $model->name;
$perm->description = $model->description;
$perm->ruleName = $model->ruleName;
$perm->data = $model->data;
Yii::$app->authManager->add($perm);
return $this->redirect(['index']);
}
return $this->render('create', ['model' => $model]);
}
public function actionUpdate($name) {
$model = $this->findModel($name);
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$perm = new Permission();
$perm->name = $model->name;
$perm->description = $model->description;
$perm->ruleName = $model->ruleName;
$perm->data = $model->data;
Yii::$app->authManager->update($name, $perm);
return $this->redirect(['index']);
}
return $this->render('update', ['model' => $model]);
}
public function actionDelete($name) {
$perm = Yii::$app->authManager->getPermission($name);
if ($perm) Yii::$app->authManager->remove($perm);
return $this->redirect(['index']);
}
protected function findModel($name) {
$perm = Yii::$app->authManager->getPermission($name);
if (!$perm) throw new \yii\web\NotFoundHttpException();
$model = new PermissionForm();
$model->name = $perm->name;
$model->description = $perm->description;
$model->ruleName = $perm->ruleName;
$model->data = $perm->data;
return $model;
}
}
?>
<?php
namespace app\controllers;
use Yii;
use yii\rbac\Role;
use app\models\RoleForm;
class RoleController extends \yii\web\Controller {
public function actionCreate() {
$model = new RoleForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$role = new Role();
$role->name = $model->name;
$role->description = $model->description;
Yii::$app->authManager->add($role);
return $this->redirect(['index']);
}
$model->child = $this->getRolePermissions($model->name);
$permissions = Yii::$app->authManager->getPermissions();
return $this->render('create', ['model' => $model, 'permissions' => $permissions]);
}
public function actionUpdate($name) {
$model = $this->findModel($name);
$oldPermissions = $this->getRolePermissions($name);
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$newPermissions = Yii::$app->request->post('AuthItem')['child'] ?? [];
if (empty($newPermissions)) {
Yii::$app->session->setFlash('error', '角色至少需要一個權限');
return $this->refresh();
}
// 先移除舊關聯
if (!empty($oldPermissions)) {
Yii::$app->authManager->removeChildren($model);
}
// 建立新關聯
$role = Yii::$app->authManager->getRole($name);
foreach ($newPermissions as $permName) {
$perm = Yii::$app->authManager->getPermission($permName);
if ($perm) Yii::$app->authManager->addChild($role, $perm);
}
return $this->redirect(['index']);
}
$model->child = $oldPermissions;
$permissions = Yii::$app->authManager->getPermissions();
return $this->render('update', ['model' => $model, 'permissions' => $permissions]);
}
public function actionDelete($name) {
$role = Yii::$app->authManager->getRole($name);
if ($role) {
// 遞歸移除子權限
$children = Yii::$app->authManager->getChildren($role);
foreach ($children as $child) {
$perm = Yii::$app->authManager->getPermission($child->name);
if ($perm) Yii::$app->authManager->removeChild($role, $perm);
}
Yii::$app->authManager->remove($role);
}
return $this->redirect(['index']);
}
private function getRolePermissions(string $roleName): array {
$role = Yii::$app->authManager->getRole($roleName);
if (!$role) return [];
$children = Yii::$app->authManager->getChildren($role);
return array_column($children, 'name');
}
protected function findModel($name) {
$role = Yii::$app->authManager->getRole($name);
if (!$role) throw new \yii\web\NotFoundHttpException();
$model = new RoleForm();
$model->name = $role->name;
$model->description = $role->description;
return $model;
}
}
?>
<?php
// 例如在 UserController::actionAssignRole()
$roleName = Yii::$app->request->post('role');
$userId = Yii::$app->request->post('user_id');
$role = Yii::$app->authManager->getRole($roleName);
if ($role) {
// 移除舊角色(單角色系統)
Yii::$app->authManager->revokeAll($userId);
// 指派新角色
Yii::$app->authManager->assign($role, $userId);
Yii::$app->session->setFlash('success', '角色指派成功');
}
?>
<?php
// 基底控制器
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\filters\AccessControl;
class BaseController extends Controller {
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['@'], // 登入用戶
'matchCallback' => function ($rule, $action) {
$perm = $action->uniqueId; // 如 'user/index'
return Yii::$app->user->can($perm);
}
],
],
],
];
}
}
?>
或直接在動作中:if (!Yii::$app->user->can('order/create')) throw new ForbiddenHttpException();
| 操作 | 方法 |
|---|---|
| 建立角色 | `$auth->add(new Role(['name'=>'admin']))` |
| 建立權限 | `$auth->add(new Permission(['name'=>'user/create']))` |
| 角色授權權限 | `$auth->addChild($role, $permission)` |
| 移除角色權限 | `$auth->removeChild($role, $permission)` / `removeChildren($role)` |
| 用戶指派角色 | `$auth->assign($role, $userId)` |
| 撤銷用戶角色 | `$auth->revoke($role, $userId)` / `revokeAll($userId)` |
| 檢查權限 | `Yii::$app->user->can('permissionName')` 或 `$auth->checkAccess($userId, 'perm')` |
| 獲取用戶所有權限 | `$auth->getPermissionsByUser($userId)` |
| 獲取角色所有權限 | `$auth->getChildren($role)` |
更多 Yii2 RBAC 進階:Rule 類實作業務規則(如擁有者才能編輯)、多租戶權限隔離、權限快取優化、API 權限範圍控制、動態菜單渲染、審計日誌整合、Casbin 整合替代方案,請參考本站專題系列。