RBAC в Yii2: роли и разрешения без боли на проде
Как настроить RBAC в Yii2 через DbManager: иерархия ролей, правила по владельцу записи, кеширование и типичные ошибки, из-за которых can() возвращает не то, что вы ждёте.

Метод Yii::$app->user->can('updatePost') вернул false, хотя пользователь — администратор, а роль назначена. Знакомая ситуация: RBAC в Yii2 работает предсказуемо ровно до того момента, пока вы не добавили правило (Rule), не забыли передать параметры в can() или не оставили включённым кеш после смены иерархии. Разберём, как собрать роли и разрешения на DbManager, где это ломается и что проверять в проде. Версия — Yii2 (2.0.x), актуальная на момент написания.
Что даёт RBAC в Yii2 и из чего он состоит
RBAC (Role-Based Access Control) в Yii2 — это не просто «роль admin может всё». Модель состоит из трёх типов сущностей, и путаница между ними — источник половины проблем.
- Permission (разрешение) — атомарное право на действие:
createPost,updatePost,deletePost. - Role (роль) — набор разрешений и других ролей:
author,admin. - Rule (правило) — PHP-класс, который во время проверки решает, применимо ли разрешение в конкретном контексте. Именно правило отвечает на вопрос «а это его собственный пост?».
Проверка идёт через единый метод can(). Yii обходит иерархию: если у пользователя есть роль, а роль содержит разрешение, и привязанное к нему правило вернуло true, доступ разрешён.
Permission и Role в Yii2 наследуют общий класс yii\rbac\Item и различаются только полем type. Технически иерархию можно строить как угодно, но по соглашению разрешения — листья дерева, роли — узлы.
DbManager или PhpManager: что выбирать
Yii2 предлагает два менеджера прав. Выбор влияет на то, где живут назначения ролей и насколько больно их менять.
| Критерий | PhpManager | DbManager |
|---|---|---|
| Хранение | PHP-файлы в @app/rbac | Таблицы в БД |
| Назначение ролей пользователям | Правкой файла, деплой | Во время работы, из кода/админки |
| Динамические роли для новых юзеров | Неудобно | Штатно |
| Скорость чтения | Быстро (файл в памяти) | Запросы к БД + кеш |
| Где уместно | Фиксированный набор ролей | Пользователи регистрируются и получают роли на лету |
Если у вас есть регистрация и роли назначаются пользователям в рантайме — берите DbManager. PhpManager подходит, когда весь набор назначений известен на этапе разработки и меняется через деплой. Дальше разбираем DbManager, потому что именно он даёт неочевидные грабли.
Подключение DbManager
В конфигурации приложения (config/web.php и config/console.php — RBAC нужен и в консоли для миграций-сидов):
// config/web.php
'components' => [
'authManager' => [
'class' => 'yii\rbac\DbManager',
// По умолчанию используются стандартные имена таблиц:
// auth_item, auth_item_child, auth_assignment, auth_rule
],
// ...
],
Таблицы создаёт встроенная миграция фреймворка. Она лежит в @yii/rbac/migrations:
php yii migrate --migrationPath=@yii/rbac/migrations
После этого в БД появятся четыре таблицы. Если миграция прошла — команда покажет Migrated up successfully.
Собираем иерархию: разрешения, роли, правило
Иерархию не создают руками в SQL — это делают программно, обычно через консольную команду или миграцию. Пример: авторы могут редактировать и удалять только свои посты, администратор — любые.
Правило «владелец записи»
Правило — это класс, наследующий yii\rbac\Rule. Метод execute() получает ID пользователя, сам item и массив параметров из вызова can().
<?php
// rbac/AuthorRule.php
namespace app\rbac;
use yii\rbac\Rule;
class AuthorRule extends Rule
{
public $name = 'isAuthor';
/**
* @param int $userId ID проверяемого пользователя
* @param \yii\rbac\Item $item роль или разрешение
* @param array $params параметры из вызова can()
* @return bool
*/
public function execute($userId, $item, $params)
{
// Ждём, что в can() передали модель поста ключом 'post'
return isset($params['post'])
? $params['post']->created_by == $userId
: false;
}
}
Команда, которая наполняет RBAC
<?php
// commands/RbacController.php
namespace app\commands;
use Yii;
use yii\console\Controller;
use app\rbac\AuthorRule;
class RbacController extends Controller
{
public function actionInit()
{
$auth = Yii::$app->authManager;
// Полная очистка, чтобы команда была идемпотентной
$auth->removeAll();
// Разрешения
$createPost = $auth->createPermission('createPost');
$createPost->description = 'Создать пост';
$auth->add($createPost);
$updatePost = $auth->createPermission('updatePost');
$updatePost->description = 'Редактировать пост';
$auth->add($updatePost);
// Правило и разрешение, привязанное к нему
$rule = new AuthorRule();
$auth->add($rule);
$updateOwnPost = $auth->createPermission('updateOwnPost');
$updateOwnPost->description = 'Редактировать свой пост';
$updateOwnPost->ruleName = $rule->name;
$auth->add($updateOwnPost);
// updateOwnPost наследует updatePost:
// кто может править свой пост, тот вообще умеет править
$auth->addChild($updateOwnPost, $updatePost);
// Роли
$author = $auth->createRole('author');
$auth->add($author);
$auth->addChild($author, $createPost);
$auth->addChild($author, $updateOwnPost);
$admin = $auth->createRole('admin');
$auth->add($admin);
$auth->addChild($admin, $updatePost); // без правила — любой пост
$auth->addChild($admin, $author); // admin включает author
echo "RBAC создан\n";
}
}
Запуск: php yii rbac/init. После него в таблице auth_item будут роли и разрешения, в auth_item_child — связи. Проверка успеха: команда вывела RBAC создан, а в БД непусто.
Назначение роли пользователю
$auth = Yii::$app->authManager;
$authorRole = $auth->getRole('author');
$auth->assign($authorRole, $user->id);
Строка попадёт в auth_assignment. Это и есть то, чего не умеет удобно PhpManager.
Проверка доступа: где теряется контекст
Ключевой момент, на котором спотыкаются: правило isAuthor не сработает, если в can() не передать пост.
// НЕ сработает: правилу нечего проверять, execute() вернёт false
Yii::$app->user->can('updateOwnPost');
// Правильно: передаём модель, которую ждёт AuthorRule
Yii::$app->user->can('updateOwnPost', ['post' => $post]);
В контроллере это обычно выглядит так:
public function actionUpdate($id)
{
$post = $this->findModel($id);
if (!Yii::$app->user->can('updateOwnPost', ['post' => $post])) {
throw new \yii\web\ForbiddenHttpException('Нет доступа к посту');
}
// ... обычная логика обновления
}
Администратор при этом пройдёт по ветке admin → updatePost без правила — ему пост передавать не обязательно, но и передача не помешает.
Правило в RBAC — это не «фильтр после проверки роли», а часть самой проверки. Забыли передать параметр — правило вернёт false, и никакая роль это не спасёт.
Кеширование: главный источник «почему не применилось»
У DbManager есть свойство cache. Если его включить, менеджер кеширует всю иерархию ролей и разрешений (но не назначения пользователям — assignments читаются из БД).
'authManager' => [
'class' => 'yii\rbac\DbManager',
'cache' => 'cache', // id компонента кеша
],
Плюс: can() перестаёт бить в БД за структурой на каждом запросе. Минус, который стоит нервов: после rbac/init или любого изменения иерархии старая структура остаётся в кеше. Вы правите роли, а can() отвечает по-старому.
После изменения иерархии инвалидируйте кеш RBAC. Менеджер делает это сам при вызовах вроде add(), remove(), addChild() в том же процессе, но если структуру меняли в консоли, а веб-процесс держит свой кеш — сбросьте вручную или через authManager->invalidateCache() в той же команде, что меняет иерархию.
Если не получилось
can()всегда возвращаетfalse, даже у admin. Скорее всего роль не назначена: проверьте, есть ли строка вauth_assignmentс нужнымuser_id. Частая причина —user_idв назначении не совпадает с тем, что возвращаетYii::$app->user->id(тип, guest-пользователь).- Разрешение с правилом не срабатывает. В
can()не передан параметр, который ждётexecute(). Проверьте ключ массива: в примере это'post', и он должен совпадать с тем, что читает правило. - Изменил иерархию — эффекта нет. Включён кеш RBAC, а он не инвалидирован. Сбросьте кеш приложения или перезапустите процесс.
Class "app\rbac\AuthorRule" not foundприcan(). Правило хранится в БД по имени класса. Если вы переименовали или переместили класс, старое имя вauth_ruleбольше не резолвится. Пересоздайте правило.- В консоли
authManagerнедоступен. Компонент не прописан вconfig/console.php. RBAC-команды используют консольную конфигурацию, а не веб.
Что смотреть дальше
Когда базовая иерархия работает, следующий шаг — фильтр доступа на уровне контроллеров через yii\filters\AccessControl с проверкой ролей в правилах rules(), чтобы не дублировать can() в каждом действии. Отдельная тема — DI-роль по умолчанию для гостей и авторизованных через свойство defaultRoles у DbManager; здесь мы её не разворачивали. В проде мерьте число запросов к четырём таблицам RBAC на страницу: без включённого кеша иерархии их количество растёт вместе с глубиной дерева ролей.
Защитник прав потребителей: Досудебные претензии на возврат товара со ссылками на ЗоЗПП.
Спросить за 15 ₽Источники: Yii2 Guide — Authorization (RBAC), Yii2 API — yii\rbac\DbManager
Частые вопросы
У меня другая минорная версия Yii2, код подойдёт?
Почему can() возвращает false, хотя роль точно назначена?
Правило (Rule) обязательно для RBAC?
Чем PhpManager отличается от DbManager на практике?
Кеш RBAC ускоряет проверку прав пользователя?
Можно ли редактировать таблицы auth_* напрямую в SQL?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.