Контроль доступа на основе ролей (rbac) ¶
Управление доступом на основе ролей (RBAC) обеспечивает простой, но мощный централизованный контроль доступа.
Пожалуйста, обратитесь к Wikipedia
для получения информации о сравнении RBAC с другими, более традиционными, системами контроля доступа.
Yii реализует общую иерархическую RBAC, следуя NIST RBAC model.
Обеспечивается функциональность RBAC через компонент приложенияauthManager.
Использование RBAC состоит из двух частей. Первая часть — это создание RBAC данных авторизации, и вторая часть — это
использование данных авторизации для проверки доступа в том месте, где это нужно.
Для облегчения последующего описания, мы сначала введём некоторые основные понятия RBAC.
Основные концепции ¶
Роль представляет собой набор разрешений (permissions) (например, создание сообщения, обновление сообщения).
Роль может быть назначена на одного или многих пользователей. Чтобы проверить, имеет ли пользователь указанные
разрешения, мы должны проверить, назначена ли пользователю роль, которая содержит данное разрешение.
С каждой ролью или разрешением может быть связано правило (rule). Правило представляет собой кусок кода, который будет
выполняться в ходе проверки доступа для определения может ли быть применена соответствующая роль или разрешение
к текущему пользователю.
Например, разрешение “обновление поста” может иметь правило, которое проверяет является ли
текущий пользователь автором поста. Во время проверки доступа, если пользователь не является автором поста, он/она будет
считаться не имеющими разрешения “обновление поста”.
И роли, и разрешения могут быть организованы в иерархию. В частности, роль может содержать другие роли или разрешения; и
разрешения могут содержать другие разрешения. Yii реализует частично упорядоченную иерархию, которая включает в себя
специальные деревья иерархии. Роль может содержать разрешение, но обратное не верно.
Что получилось
В итоге, на этот раз мы изменили и добавили файлы:
Building authorization data
Building authorization data is all about the following tasks:
Depending on authorization flexibility requirements the tasks above could be done in different ways.
If your permissions hierarchy is meant to be changed by developers only, you can use either migrations
or a console command. Migration pro is that it could be executed along with other migrations.
Either way in the end you’ll get the following RBAC hierarchy:
In case you need permissions hierarchy to be formed dynamically you need a UI or a console command. API used to
build the hierarchy itself won’t be different.
Configuring rbac
Before we set off to define authorization data and perform access checking, we need to configure the
[[yiibaseApplication::authManager|authManager]] application component. Yii provides two types of authorization managers:
[[yiirbacPhpManager]] and [[yiirbacDbManager]].
Role based access control (rbac)
Role-Based Access Control (RBAC) provides a simple yet powerful centralized access control. Please refer to
the Wikipedia for details about comparing RBAC
with other more traditional access control schemes.
Yii implements a General Hierarchical RBAC, following the NIST RBAC model.
It provides the RBAC functionality through the [[yiirbacManagerInterface|authManager]] application component.
Using RBAC involves two parts of work. The first part is to build up the RBAC authorization data, and the second
part is to use the authorization data to perform access check in places where it is needed.
To facilitate our description next, we will first introduce some basic RBAC concepts.
Using dbmanager
The following code shows how to configure the authManager in the application configuration using the [[yiirbacDbManager]] class:
Note: If you are using yii2-basic-app template, there is a config/console.php
configuration file where the
authManager
needs to be declared additionally to config/web.php
.
In case of yii2-advanced-app the authManager
should be declared only once in common/config/main.php
.
DbManager uses four database tables to store its data:
- [[yiirbacDbManager::$itemTable|itemTable]]: the table for storing authorization items. Defaults to “auth_item”.
- [[yiirbacDbManager::$itemChildTable|itemChildTable]]: the table for storing authorization item hierarchy. Defaults to “auth_item_child”.
- [[yiirbacDbManager::$assignmentTable|assignmentTable]]: the table for storing authorization item assignments. Defaults to “auth_assignment”.
- [[yiirbacDbManager::$ruleTable|ruleTable]]: the table for storing rules. Defaults to “auth_rule”.
Before you can go on you need to create those tables in the database. To do this, you can use the migration stored in @yii/rbac/migrations:
yii migrate –migrationPath=@yii/rbac/migrations
Read more about working with migrations from different namespaces in
Separated Migrations section.
The authManager can now be accessed via Yii::$app->authManager.
Using migrations
You can use migrations
to initialize and modify hierarchy via APIs offered by authManager.
Create new migration using ./yii migrate/create init_rbac then implement creating a hierarchy:
Using phpmanager
The following code shows how to configure the authManager in the application configuration using the [[yiirbacPhpManager]] class:
The authManager can now be accessed via Yii::$app->authManager.
By default, [[yiirbacPhpManager]] stores RBAC data in files under @app/rbac directory. Make sure the directory
and all the files in it are writable by the Web server process if permissions hierarchy needs to be changed online.
Автозаполнение формы обратной связи
Если теперь авторизованный пользователь войдёт на страницу обратной связи, то ему всё равно нужно будет заполнить поля имени и электронного адреса. Добавим автоподстановку значений в поля формы:
Теперь авторизованному пользователю будет легче.
Использование правил ¶
Как упомянуто выше, правила добавляют дополнительные ограничения на роли и разрешения. Правила — это классы, расширяющие
yiirbacRule. Они должны реализовывать метод execute(). В иерархии, созданной нами ранее,
автор не может редактировать свой пост. Давайте исправим это. Сначала мы должны создать правило, проверяющее
что пользователь является автором поста:
Использование роли по умолчанию ¶
Роль по умолчанию — это роль, которая неявно присваивается всем пользователям. Вызов метода
yiirbacManagerInterface::assign() не требуется, и авторизационные данные не содержат информации о назначении.
Роль по умолчанию обычно связывают с правилом, определяющим к какой роли принадлежит каждый пользователь.
Роли по умолчанию обычно используются в приложениях, которые уже имеют какое-то описание ролей. Для примера, приложение
может иметь столбец “group” в таблице пользователей, и каждый пользователь принадлежит к какой-то группе. Если каждая
группа может быть сопоставлена роли в модели RBAC, вы можете использовать роль по умолчанию для автоматического назначения
каждому пользователю роли RBAC. Давайте используем пример, чтобы понять как это можно сделать.
Как сделать авторизацию и регистрацию в yii2 basic
<?php
namespaceappmodels;
useYii;
useyiibaseNotSupportedException;
useyiibehaviorsTimestampBehavior;
useyiidbActiveRecord;
useyiiwebIdentityInterface;
/**
* User model
*
* @property integer $id
* @property string $username
* @property string $password_hash
* @property string $password_reset_token
* @property string $email
* @property string $auth_key
* @property integer $status
* @property integer $created_at
* @property integer $updated_at
* @property string $password write-only password
*/
classUserextendsActiveRecordimplementsIdentityInterface
{
constSTATUS_DELETED=0;
constSTATUS_ACTIVE=10;
/**
* @inheritdoc
*/
publicstaticfunctiontableName()
{
return‘{{%user}}’;
}
/**
* @inheritdoc
*/
publicfunctionbehaviors()
{
return[
TimestampBehavior::className(),
];
}
/**
* @inheritdoc
*/
publicfunctionrules()
{
return[
[‘status’,‘default’,‘value’=>self::STATUS_ACTIVE],
[‘status’,‘in’,‘range’=>[self::STATUS_ACTIVE,self::STATUS_DELETED]],
];
}
/**
* @inheritdoc
*/
publicstaticfunctionfindIdentity($id)
{
returnstatic::findOne([‘id’=>$id,‘status’=>self::STATUS_ACTIVE]);
}
/**
* @inheritdoc
*/
publicstaticfunctionfindIdentityByAccessToken($token,$type=null)
{
thrownewNotSupportedException(‘”findIdentityByAccessToken” is not implemented.’);
}
/**
* Finds user by username
*
* @param string $username
* @return static|null
*/
publicstaticfunctionfindByUsername($username)
{
returnstatic::findOne([‘username’=>$username,‘status’=>self::STATUS_ACTIVE]);
}
/**
* @inheritdoc
*/
publicfunctiongetId()
{
return$this->getPrimaryKey();
}
/**
* @inheritdoc
*/
publicfunctiongetAuthKey()
{
return$this->auth_key;
}
/**
* @inheritdoc
*/
publicfunctionvalidateAuthKey($authKey)
{
return$this->getAuthKey()===$authKey;
}
/**
* Validates password
*
* @param string $password password to validate
* @return bool if password provided is valid for current user
*/
publicfunctionvalidatePassword($password)
{
returnYii::$app->security->validatePassword($password,$this->password_hash);
}
/**
* Generates password hash from password and sets it to the model
*
* @param string $password
*/
publicfunctionsetPassword($password)
{
$this->password_hash=Yii::$app->security->generatePasswordHash($password);
}
/**
* Generates “remember me” authentication key
*/
publicfunctiongenerateAuthKey()
{
$this->auth_key=Yii::$app->security->generateRandomString();
}
//ВОССТАНОВЛЕНИЕ ПАРОЛЯ
publicstaticfunctionfindByPasswordResetToken($token)
{
if(!static::isPasswordResetTokenValid($token)){
returnnull;
}
returnstatic::findOne([
‘password_reset_token’=>$token,
‘status’=>self::STATUS_ACTIVE,
]);
}
publicstaticfunctionisPasswordResetTokenValid($token)
{
if(empty($token)){
returnfalse;
}
$timestamp=(int)substr($token,strrpos($token,‘_’) 1);
$expire=Yii::$app->params[‘user.passwordResetTokenExpire’];
return$timestamp $expire>=time();
}
publicfunctiongeneratePasswordResetToken()
{
$this->password_reset_token=Yii::$app->security->generateRandomString().‘_’.time();
}
publicfunctionremovePasswordResetToken()
{
$this->password_reset_token=null;
}
}
Консольное управление
Теперь у нас имеется таблица для хранения пользователей и вся инфраструктура для авторизации. Теперь можно попробовать зарегистрироваться на своём же сайте. Но удобнее добавить свою консольную команду для управления пользователями. Добавим в неё примитивный набор действий:
namespaceappcommands; useappmodulesusermodelsUser; useyiibaseModel; useyiiconsoleController; useyiiconsoleException; useyiihelpersConsole; classUsersControllerextendsController{publicfunctionactionIndex(){echo'yii users/create' . PHP_EOL; echo'yii users/remove' . PHP_EOL; echo'yii users/activate' . PHP_EOL; echo'yii users/change-password' . PHP_EOL; }publicfunctionactionCreate(){$model = newUser(); $this->readValue($model, 'username'); $this->readValue($model, 'email'); $model->setPassword($this->prompt('Password:', ['required' => true, 'pattern' => '#^.{6,255}$#i', 'error' => 'More than 6 symbols', ])); $model->generateAuthKey(); $this->log($model->save()); }publicfunctionactionRemove(){$username = $this->prompt('Username:', ['required' => true]); $model = $this->findModel($username); $this->log($model->delete()); }publicfunctionactionActivate(){$username = $this->prompt('Username:', ['required' => true]); $model = $this->findModel($username); $model->status = User::STATUS_ACTIVE; $model->removeEmailConfirmToken(); $this->log($model->save()); }publicfunctionactionChangePassword(){$username = $this->prompt('Username:', ['required' => true]); $model = $this->findModel($username); $model->setPassword($this->prompt('New password:', ['required' => true, 'pattern' => '#^.{6,255}$#i', 'error' => 'More than 6 symbols', ])); $this->log($model->save()); } @param @throws @return privatefunctionfindModel($username){if(!$model = User::findOne(['username' => $username])){thrownewException('User not found'); }return$model; } @param @param privatefunctionreadValue($model, $attribute){$model->$attribute = $this->prompt(mb_convert_case($attribute, MB_CASE_TITLE, 'utf-8') . ':', ['validator' => function($input, &$error)use($model, $attribute){$model->$attribute = $input; if($model->validate([$attribute])){returntrue; }else{$error = implode(',', $model->getErrors($attribute)); returnfalse; }}, ]); } @param privatefunctionlog($success){if($success){$this->stdout('Success!', Console::FG_GREEN, Console::BOLD); }else{$this->stderr('Error!', Console::FG_RED, Console::BOLD); }echoPHP_EOL; }}
Теперь можно выполнить:
Настройка authmanager с помощью dbmanager¶
Следующий код показывает как настроить в конфигурации приложения authManager с использованием класса yiirbacDbManager:
return [
'components' => [
'authManager' => [
'class' => 'yiirbacDbManager',
],
],
];
Примечание: Если вы используете шаблон проекта basic, компонент authManager
необходимо настроить как в config/web.php
, так и в
конфигурации консольного приложенияconfig/console.php
.
При использовании шаблона проекта advanced authManager
достаточно настроить единожды в common/config/main.php
.
DbManager использует четыре таблицы для хранения данных:
Прежде чем вы начнёте использовать этот менеджер, вам нужно создать таблицы в базе данных. Чтобы сделать это,
вы можете использовать миграцию хранящуюся в файле @yii/rbac/migrations:
yii migrate –migrationPath=@yii/rbac/migrations
Теперь authManager может быть доступен через Yii::$app->authManager.
Настройка authmanager с помощью phpmanager¶
Следующий код показывает как настроить в конфигурации приложения authManager с использованием класса yiirbacPhpManager:
return [
'components' => [
'authManager' => [
'class' => 'yiirbacPhpManager',
],
],
];
Теперь authManager может быть доступен через Yii::$app->authManager.
Замечание: По умолчанию, yiirbacPhpManager сохраняет данные RBAC в файлах в директории @app/rbac/
. Убедитесь
что данная директория и файлы в них доступны для записи Web серверу, если иерархия разрешений должна меняться онлайн.
Настройка rbac manager ¶
Перед определением авторизационных данных и проверкой прав доступа, мы должны настроить компонент приложения
yiibaseApplication::authManager. Yii предоставляет два типа менеджеров авторизации:
yiirbacPhpManager и yiirbacDbManager.
Создание данных авторизации ¶
Для создания данных авторизации нужно выполнить следующие задачи:
- определение ролей и разрешений;
- установка отношений между ролями и правами доступа;
- определение правил;
- связывание правил с ролями и разрешениями;
- назначение ролей пользователям.
В зависимости от требований к гибкости авторизации перечисленные задачи могут быть выполнены разными путями.
Если иерархия прав не меняется, и количество пользователей зафиксировано, вы можете создать
консольную команду, которая будет единожды инициализировать данные
через APIs, предоставляемое authManager:
Структура данных для таблицы
Можно создать таблицы базы вручную, но с этим будет много мороки. Нужно будет постоянно сравнивать рабочую и локальную базы и вносить в них каждый раз одинаковые изменения. Издеваться над собой не в наших планах, поэтому воспользуемся миграциями. Учтём, что в Yii2 немного другой формат запуска.
Запустим команду создания первой миграции и ответим yes или y:
Фильтры контроля доступа ¶
Фильтры контроля доступа (ACF) являются простым методом, который лучше всего использовать в приложениях с простым
контролем доступа. Как видно из их названия, ACF — это фильтры, которые могут присоединяться к контроллеру
или модулю как поведение. ACF проверяет набор правил доступа, чтобы убедиться,
что пользователь имеет доступ к запрошенному действию.
Код ниже показывает, как использовать ACF фильтр, реализованный в yiifiltersAccessControl:
useyiifiltersAccessControl;
classSiteControllerextendsController{
publicfunctionbehaviors(){
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['login', 'logout', 'signup'],
'rules' => [
[
'allow' => true,
'actions' => ['login', 'signup'],
'roles' => ['?'],
],
[
'allow' => true,
'actions' => ['logout'],
'roles' => ['@'],
],
],
],
];
}
}
Код выше показывает ACF фильтр, связанный с контроллером site через поведение. Это типичный способ использования фильтров действий.
Параметр only указывает, что фильтр ACF нужно применять только к действиям login, logout и signup.
Параметр rules задаёт правила доступа, которые означают следующее:
- Разрешить всем гостям (ещё не прошедшим авторизацию) доступ к действиям
login
иsignup
.
Опцияroles
содержит знак вопроса?
, это специальный токен обозначающий “гостя”. - Разрешить аутентифицированным пользователям доступ к действию
logout
. Символ@
— это другой специальный токен,
обозначающий аутентифицированного пользователя.
Когда фильтр ACF проводит проверку авторизации, он проверяет правила по одному сверху вниз, пока не найдёт совпадение.
Значение опции allow выбранного правила указывает, авторизовывать пользователя или нет. Если ни одно из правил
не совпало, то пользователь считается НЕавторизованным, и фильтр ACF останавливает дальнейшее выполнение действия.
По умолчанию, когда у пользователя отсутствует доступ к текущему действию, ACF делает следующее:
Вы можете переопределить это поведение, настроив свойство yiifiltersAccessControl::$denyCallback:
[
'class' => AccessControl::className(),
'denyCallback' => function($rule, $action){
thrownew Exception('У вас нет доступа к этой странице');
}
]
Правила доступа поддерживают набор свойств. Ниже дано краткое описание поддерживаемых опций.
Вы также можете расширить yiifiltersAccessRule, чтобы создать свой собственный класс правил доступа.