34Авторизация: ACF и RBAC
Фильтр AccessControl с правилами allow/deny по ролям, IP, методам и колбэкам; RBAC — роли, разрешения, иерархия, правила (Rule), PhpManager и DbManager, консольная инициализация, назначение ролей, проверка can(), роли по умолчанию.
Аутентификация сказала, кто пользователь; авторизация решает, что ему можно. Два инструмента: ACF (Access Control Filter) — простые правила у контроллера, и RBAC — управление доступом на основе ролей с иерархией и правилами, когда прав много и они зависят от данных.
Access Control Filter#
use yii\filters\AccessControl;
class SiteController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'only' => ['login', 'logout', 'signup'], // к каким действиям применять
'rules' => [
[
'allow' => true,
'actions' => ['login', 'signup'],
'roles' => ['?'], // только гости
],
[
'allow' => true,
'actions' => ['logout'],
'roles' => ['@'], // только вошедшие
],
],
'denyCallback' => function ($rule, $action) {
throw new \Exception('Вам сюда нельзя');
},
],
];
}
}Правила проверяются сверху вниз до первого совпадения. Совпало allow — пропустить, deny — запретить. Ни одно не совпало — запретить (гостю — редирект на логин, вошедшему — 403).
Опции правила#
allowtrue разрешить / false запретитьactionscontrollersadmin/user); пусто — текущийroles? гость, @ аутентифицирован, иначе — роль/разрешение RBAC (Yii::$app->user->can())roleParamscan() (массив или замыкание)ips* в конце (192.168.*)verbsGET, POST)matchCallbackfunction ($rule, $action) — любое условиеdenyCallback[
'allow' => true,
'actions' => ['special-callback'],
'matchCallback' => function ($rule, $action) {
return date('d-m') === '31-10'; // только в Хэллоуин
},
],
[
'allow' => true,
'actions' => ['update'],
'roles' => ['updatePost'],
'roleParams' => function ($rule) {
return ['post' => Post::findOne(['id' => Yii::$app->request->get('id')])];
},
],RBAC#
Модель: разрешение (permission) — действие («создать пост»), роль (role) — набор разрешений и других ролей, правило (rule) — дополнительная проверка с параметрами («автор ли пользователь этого поста»). Пользователю назначаются роли/разрешения; проверка — Yii::$app->user->can('updatePost', ['post' => $post]).
Настройка#
'components' => [
'authManager' => [
'class' => 'yii\rbac\DbManager', // таблицы: auth_item, auth_item_child, auth_assignment, auth_rule
// 'class' => 'yii\rbac\PhpManager', // файлы в @app/rbac — для небольших статичных схем
// 'defaultRoles' => ['guest'],
// 'cache' => 'cache', // кэшировать иерархию (DbManager)
],
],Для DbManager: ./yii migrate --migrationPath=@yii/rbac/migrations. Компонент должен быть в обеих конфигурациях — web и console.
Построение иерархии#
class RbacController extends \yii\console\Controller
{
public function actionInit()
{
$auth = Yii::$app->authManager;
$auth->removeAll();
// разрешения
$createPost = $auth->createPermission('createPost');
$createPost->description = 'Создание постов';
$auth->add($createPost);
$updatePost = $auth->createPermission('updatePost');
$auth->add($updatePost);
// роли
$author = $auth->createRole('author');
$auth->add($author);
$auth->addChild($author, $createPost);
$admin = $auth->createRole('admin');
$auth->add($admin);
$auth->addChild($admin, $updatePost);
$auth->addChild($admin, $author); // admin наследует всё от author
// назначения (обычно — при регистрации)
$auth->assign($author, 2);
$auth->assign($admin, 1);
}
}./yii rbac/initИерархия: admin → author → createPost; admin → updatePost. can('createPost') для admin вернёт true через цепочку.
Правила — доступ с учётом данных#
«Автор может редактировать свой пост»:
namespace app\rbac;
use yii\rbac\Rule;
class AuthorRule extends Rule
{
public $name = 'isAuthor';
public function execute($user, $item, $params)
{
return isset($params['post']) ? $params['post']->createdBy == $user : false;
}
}$rule = new \app\rbac\AuthorRule();
$auth->add($rule);
$updateOwnPost = $auth->createPermission('updateOwnPost');
$updateOwnPost->description = 'Редактирование своего поста';
$updateOwnPost->ruleName = $rule->name;
$auth->add($updateOwnPost);
$auth->addChild($updateOwnPost, $updatePost); // updateOwnPost «использует» updatePost
$auth->addChild($author, $updateOwnPost);// проверка
if (Yii::$app->user->can('updatePost', ['post' => $post])) { … }Для автора цепочка author → updateOwnPost(правило) → updatePost — правило проверит createdBy. Для admin путь admin → updatePost без правила — можно всегда.
Назначение ролей#
$auth->assign($auth->getRole('author'), $user->id); // при регистрации — в afterSave или в контроллере
$auth->revoke($role, $userId);
$auth->revokeAll($userId);
$auth->getRolesByUser($userId);
$auth->getPermissionsByUser($userId);
$auth->getUserIdsByRole('admin');
$auth->checkAccess($userId, 'updatePost', $params); // то же, что user->can(), но для любого IDРоли по умолчанию#
Роль, которая есть у всех (в т.ч. гостей) без записи в auth_assignment — через defaultRoles и правило:
class UserGroupRule extends Rule
{
public $name = 'userGroup';
public function execute($user, $item, $params)
{
if (Yii::$app->user->isGuest) return false;
$group = Yii::$app->user->identity->group;
return $item->name === 'admin' ? $group == 1 : ($item->name === 'author' ? $group == 1 || $group == 2 : false);
}
}
// authManager: 'defaultRoles' => ['admin', 'author'],Так роль определяется полем group пользователя, а не таблицей назначений.
Проверка в коде#
if (Yii::$app->user->can('createPost')) { … } // роль или разрешение
if (Yii::$app->user->can('updatePost', ['post' => $post])) { … }
if (!Yii::$app->user->can('admin')) {
throw new \yii\web\ForbiddenHttpException('Нет доступа');
}
// в представлении
<?php if (Yii::$app->user->can('updatePost', ['post' => $model])): ?>
<?= Html::a('Редактировать', ['update', 'id' => $model->id]) ?>
<?php endif ?>can() для гостя (user->id === null) проверяет только defaultRoles.
ACF или RBAC?#
- права зависят только от «гость/вошёл», IP, метода, действия;
- маленькое приложение, 1–2 роли;
- нужно быстро закрыть контроллер.
- много ролей и разрешений, иерархия;
- доступ зависит от данных (свой/чужой объект);
- права меняются администратором без деплоя (DbManager).
Они совместимы: в roles правила ACF пишут имена RBAC-элементов, и фильтр вызовет can().
Доступ запрещён: гость — редирект на loginUrl, вошедший — ForbiddenHttpException (403).
? — неаутентифицированный пользователь, @ — аутентифицированный.
Разрешение — конкретное действие; роль — набор разрешений и ролей. Проверять лучше разрешения, назначать — роли.
Для условий, зависящих от параметров: например, updateOwnPost проверяет, что автор поста — текущий пользователь.
Через defaultRoles менеджера и правило, определяющее роль по данным пользователя.