Один из самых часто встречающихся вопросов по Yii 2.0 — реализация управления доступом на основе ролей. Поддержка RBAC встроена в Yii2, но она может быть сложновата для реализации начинающими разработчиками, впервые столкнувшимися с этим вопросом при знакомстве с Yii 2.0.
Часто бывает достаточно всего двух ролей: Пользователь и Администратор. Эта реализация поможет понять как работает механизм RBAC в Yii2 и может стать отправной точкой для дальнейшего расширения функционала управления доступом на основе ролей.
Большая часть работы уже проделана разработчиками Yii 2.0. Что осталось сделать:
- Добавить константу для роли Администратора в модуль User
- Добавить эту константу в перечень значений роли пользователей
- Добавить к модели User статичный метод isUserAdmin
- Добавить к модели LoginForm метод loginAdmin
- Изменить контроллер backend так, чтобы он использовал метод loginAdmin при входе пользователя.
- Добавим правило доступа, ограничивающее доступ к странице about для всех, кроме Администратора.
За основу возьмем приложение advanced. Про установку его из стандартной поставки Yii2 можно узнать в официальном руководстве.
Добавим константу для роли Администратора
В начало модели User, common/models/User.php, добавим:
const ROLE_ADMIN = 20;
Мы будем использовать константу для хранения значения 20, указывающего на роль Администратора. Если значение поля role будет равно 20, значит субъект получает права Администратора. По-умолчанию значение этого поля будет равно 10, что означает права Пользователя для данного субъекта. На данном этапе можно изменить значение данного поля прямиком в базе данных, например, используя PhpMyAdmin.
Адаптируем правила валидации модели User
Внесем изменения в метод Rules модели User:
['role', 'in', 'range' => [self::ROLE_USER, self::ROLE_ADMIN]],
Просто ограничим значение поля роли двумя значениями, хранящимися в константах.
Добавим метод isUserAdmin
Файл common/models/User.php:
public static function isUserAdmin($username) { if (static::findOne(['username' => $username, 'role' => self::ROLE_ADMIN])) { return true; } else { return false; } }
Имя метода говорит само за себя. Используя метод findOne ищем запись с соответствующим именем и ролью Администратора. Если запись не будет найдена, возвращаем false.
Метод будет статичным, что позволит использовать его лаконично и избежать излишней путаницы в коде.
Организуем вход для Администратора
Добавим метод loginAdmin в модель commom/models/LoginForm.php:
public function loginAdmin() { if ($this->validate() && User::isUserAdmin($this->username)) { return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); } else { return false; } }
Просто добавим еще одно условие. Теперь не только валидация должна пройти успешно, но и метод User::isUserAdmin($this->username) должен вернуть true.
Настроим форму входа в backend
Внесем изменения в файл backend\controllers\SiteController.php:
public function actionLogin() { if (!\Yii::$app->user->isGuest) { return $this->goHome(); } $model = new LoginForm(); if ($model->load(Yii::$app->request->post()) && $model->loginAdmin()) { return $this->goBack(); } else { return $this->render('login', [ 'model' => $model, ]); } }
Изменение совсем небольшое, используем метод loginAdmin вместо login.
Теперь для входа в backend пользователю необходимо иметь роль Администратора, то есть значение роли должно равняться значению константы ROLE_ADMIN модели User.
Настал момент ограничить доступ к методам контроллера. Проще всего это реализовать используя поведения и метод matchCallback в правилах.
Ограничиваем доступ к методу about приложения frontend
В начале файла frontend/controllers/SiteController.php подключим модель User:
use common\models\User;
В поведение, метод behaviors, в том же файле frontend/controllers/SiteController.php добавим:
public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'only' => ['logout', 'signup', 'about'], 'rules' => [ [ 'actions' => ['signup'], 'allow' => true, 'roles' => ['?'], ], [ 'actions' => ['logout'], 'allow' => true, 'roles' => ['@'], ], [ 'actions' => ['about'], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return User::isUserAdmin(Yii::$app->user->identity->username); } ], ], ], 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'logout' => ['post'], ], ], ]; }
Что нового? Мы изменили строку:
'only' => ['logout', 'signup', 'about'],
Теперь поведение распространяется и на экшн about. Для него и добавили правило в массиве:
[ 'actions' => ['about'], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return User::isUserAdmin(Yii::$app->user->identity->username); } ],
Это правило распространяется только на метод action. Метод matchCallback используется для проверки текущего пользователя Yii::$app->user->identity->username на соответствие роли Администратора. В случае, если роль пользователя не Администратор, будет выдано предупреждение о том, что доступ запрещен.
Очевидно, что вход пользователя в систему необходим для определения его роли. И доступ не будет разрешен для пользователя не вошедшего в систему.
Такая методика будет работать с любым контроллером, где в поведении behaviors правила доступа описаны подобным образом. Данная простая реализация RBAC может быть использована в любом эшне frontend или backend приложений.
Подведем итоги
У нас получилась простая, минимальная реализация RBAC на Yii2, ее вполне достаточно во многих случаях. Yii 2.0 «из коробки» уже имеет все, что нужно для начала работы. Мало какие еще php фреймворки так дружелюбны как к начинающим, так и к опытным разработчикам. Мы добились нужного результата всего несколькими строчками кода.
Если вы хотите получить пользовательский интерфейс для управления ролями пользователей, можно создать crud для модели User в backend, используя замечательный генератор кода Gii, встроенный в Yii. Главное правильно использовать пространства имен: основная модель User находится в common\models.
Очевидно, что это минимальный функционал RBAC в Yii2. Хорошо, если достаточно только двух ролей. При разработке крупных приложений может понадобиться более сложная система управления доступом на основе ролей. Данный пример наглядно показывает, в каком направлении нужно двигаться.
Доброго времени суток! Очень помогла ваша статья настроить разделение прав по ролям.
Однако у меня в SiteController не виден класс User, и пришлось по другому реализовать проверку.
Как найти ошибку, почему у меня не виден класс User?
Нужно явно указать пространство имен для класса User:
use app\common\models\User;
или
app\common\models\User::isUserAdmin(Yii::$app->user->identity->username);
\common\models\User::isUserAdmin(Yii::$app->user->identity->username);
Вот этим длинным запросом пользуюсь, чтобы детектить админа во view.
Хочу добавить не статический метод для проверки на админа, чтобы было доступно Yii::$app->user->isAdmin()
хотя можно и Yii::$app->user->identity->role == \common\models\User::ROLE_ADMIN
Но может лучше использовать RBAC? Подсакжите)
Привет!
(сборка basic Yii2.0.7) Два вопроса:
У вас написано:
Внесем изменения в метод Rules модели User:
[‘role’, ‘in’, ‘range’ => [self::ROLE_USER, self::ROLE_ADMIN]],
1. Не нашел в User метода Rules (есть ли там вообще валидация?)
2. Не нашел объявления self::ROLE_USER
Мне кажется, что причина в приложении «basic».
Я у себя тоже не нашёл этого метода.
Шаг в сторону от мануала и уже всё не работает и ничего не получается починить )))
А для начинающих было бы здорово сделать точно такой же мануал для приложения «basic» :)
Опытные люди итак разберутся, а начинающим рекомендуют не лезть в advanced…
1. Видимо метод rules для basic нужно создавать вручную, а
2. константы ROLE_USER тоже нет в advanced, думается что её тоже можно добавить вручную
Пробуйте, будут ошибки — гугл поможет
А где в базе данных хранится идентификатор админа (20)? В таблице user нет поля role!
Где в базе данных хранится идентификатор админа (20)? Ведь в таблице user нет поля role.
уже status там, и в примере role на статус поменяйте
добавте в базу колонку role
и в файл
const STATUS_DELETED = 0;
const STATUS_ACTIVE = 10;
const ROLE_USER = 10;
const ROLE_ADMIN = 20;
Очень помогло спасибо.
А для basic нельзя было сделать. Особенно, если вы начали говорить о новичках… Ладно, буду разбираться так.. Самое сложное для меня было Рег/авт/аут, с чем я разобрался