Spec-Zone.ru › Yii 2.0

Авторизация

  1. Фильтр управления доступом
  2. Управление доступом на основе ролей (RBAC)
  3. Назначение ролей пользователям

Авторизация — это процесс проверки наличия у пользователя достаточных прав для выполнения определенного действия. Yii предоставляет два метода авторизации: фильтр управления доступом (ACF) и управление доступом на основе ролей (RBAC).

Фильтр управления доступом

Фильтр управления доступом (ACF) — это простой метод авторизации, реализованный как yii\filters\AccessControl, который лучше всего использовать в приложениях, которым требуется только базовая система управления доступом. Как следует из названия, ACF — это фильтр действия фильтр, который может использоваться в контроллере или модуле. При попытке пользователем выполнить действие, ACF проверяет список правил доступа, чтобы определить, разрешен ли пользователю доступ к запрошенному действию.

Ниже представлен пример использования ACF в контроллере site:

use yii\web\Controller;
use yii\filters\AccessControl;

class SiteController extends Controller
{
    public function behaviors()
    {
        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. Все остальные действия в контроллере site не подпадают под контроль доступа. Опция rules содержит список правил доступа, которые интерпретируются следующим образом:

  • Разрешить всем гостям (еще не авторизованным) пользователям доступ к действиям login и signup. Опция roles содержит вопросительный знак ?, который является специальным маркером, обозначающим «гостевых пользователей».
  • Разрешить авторизованным пользователям доступ к действию logout. Символ @ — это другой специальный маркер, обозначающий «аутентифицированных пользователей».

ACF выполняет проверку авторизации, последовательно проверяя правила сверху вниз, пока не найдет правило, соответствующее текущему контексту выполнения. Значение allow соответствующего правила будет использовано для определения, авторизован ли пользователь. Если ни одно из правил не соответствует, это означает, что пользователь НЕ авторизован, и ACF прекратит дальнейшее выполнение действия.

Когда ACF определяет, что пользователь не авторизован для доступа к текущему действию, по умолчанию выполняется следующее:

  • Если пользователь — гость, вызывается yii\web\User::loginRequired() для перенаправления браузера пользователя на страницу входа.
  • Если пользователь уже авторизован, генерируется исключение yii\web\ForbiddenHttpException.

Вы можете настроить это поведение, конфигурируя свойство yii\filters\AccessControl::$denyCallback следующим образом:

[
    'class' => AccessControl::className(),
    ...
    'denyCallback' => function ($rule, $action) {
        throw new \Exception('You are not allowed to access this page');
    }
]

Правила доступа поддерживают множество опций. Ниже приведен краткий обзор поддерживаемых опций. Вы также можете расширять yii\filters\AccessRule, чтобы создавать свои собственные настраиваемые классы правил доступа.

  • allow: указывает, является ли это правилом «разрешить» или «запретить».

  • actions: указывает, к каким действиям относится это правило. Должно быть массивом идентификаторов действий. Сравнение регистрозависимое. Если эта опция пустая или не задана, правило применяется ко всем действиям.

  • controllers: указывает, к каким контроллерам относится это правило. Должен быть массивом идентификаторов контроллеров. Каждый идентификатор контроллера предваряется идентификатором модуля (если таковой имеется). Сравнение регистрозависимое. Если эта опция пустая или не задана, правило применяется ко всем контроллерам.

  • roles: указывает, к каким ролям пользователей относится это правило. Распознаются две специальные роли, проверяемые через yii\web\User::$isGuest:

    • ?: соответствует гостевому пользователю (еще не авторизован).
    • @: соответствует авторизованному пользователю.

    Использование других имен ролей вызовет вызов yii\web\User::can(), что требует включения RBAC (описано в следующем подразделе). Если эта опция пустая или не задана, правило применяется ко всем ролям.

  • roleParams: задает параметры, которые будут переданы в yii\web\User::can(). См. раздел ниже, посвященный правилам RBAC, чтобы узнать, как это можно использовать. Если эта опция пустая или не задана, параметры не передаются.

  • ips: указывает, какие адреса IP-клиента соответствуют этому правилу. Адрес IP может содержать подстановочный символ * в конце, чтобы соответствовать адресам IP с тем же префиксом. Например, '192.168.*' соответствует всем адресам IP в сегменте '192.168.'. Если эта опция пустая или не задана, правило применяется ко всем адресам IP.

  • verbs: указывает, какой метод запроса (например, GET, POST) соответствует этому правилу. Сравнение нечувствительно к регистру.

  • matchCallback: указывает вызов PHP, который должен вызываться для определения, должно ли применяться это правило.

  • denyCallback: указывает вызов PHP, который должен вызываться, когда это правило отклонит доступ.

Ниже приведен пример, демонстрирующий использование опции matchCallback, которая позволяет реализовать произвольную логику проверки доступа:

use yii\filters\AccessControl;

class SiteController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'only' => ['special-callback'],
                'rules' => [
                    [
                        'actions' => ['special-callback'],
                        'allow' => true,
                        'matchCallback' => function ($rule, $action) {
                            return date('d-m') === '31-10';
                        }
                    ],
                ],
            ],
        ];
    }

    // Match callback called! This page can be accessed only each October 31st
    public function actionSpecialCallback()
    {
        return $this->render('happy-halloween');
    }
}

Управление доступом на основе ролей (RBAC)

Управление доступом на основе ролей (RBAC) предоставляет простую, но мощную централизованную систему управления доступом. Подробнее об RBAC по сравнению с другими традиционными схемами управления доступом см. на Wikipedia.

Yii реализует общую иерархическую модель RBAC, следуя модели RBAC NIST. Функциональность RBAC предоставляется компонентом authManager приложения.

Использование RBAC включает две части: построение данных авторизации RBAC и использование данных авторизации для проверки доступа в местах, где это необходимо.

Для облегчения описания в дальнейшем мы сначала представим некоторые основные понятия RBAC.

Основные понятия

Роль представляет собой набор разрешений (например, создание постов, обновление постов). Одна роль может быть назначена одному или нескольким пользователям. Для проверки наличия у пользователя определенного разрешения можно проверить, назначена ли ему роль, которая содержит это разрешение.

Каждой роли или разрешению может быть связан правило. Правило представляет собой фрагмент кода, который выполняется во время проверки доступа, чтобы определить, относится ли соответствующая роль или разрешение к текущему пользователю. Например, разрешение «обновить пост» может иметь правило, проверяющее, является ли текущий пользователь автором поста. Во время проверки доступа, если пользователь НЕ является автором поста, он/она считается не имеющим разрешения «обновить пост».

Как роли, так и разрешения могут быть организованы в иерархию. В частности, роль может состоять из других ролей или разрешений; и разрешение может состоять из других разрешений. Yii реализует иерархию частичного порядка, которая включает более специализированную иерархию дерева. Хотя роль может содержать разрешение, это не true и наоборот.

Настройка RBAC

Прежде чем приступить к определению данных авторизации и выполнению проверки доступа, необходимо настроить компонент приложения authManager. Yii предоставляет два типа менеджеров авторизации: yii\rbac\PhpManager и yii\rbac\DbManager. Первый использует файл PHP-скрипта для хранения данных авторизации, а второй хранит данные авторизации в базе данных. Рекомендуется использовать первый вариант, если ваше приложение не требует очень динамичного управления ролями и разрешениями.

Использование PhpManager

Следующий код демонстрирует настройку authManager в конфигурации приложения с помощью класса yii\rbac\PhpManager:

return [
    // ...
    'components' => [
        'authManager' => [
            'class' => 'yii\rbac\PhpManager',
        ],
        // ...
    ],
];

Теперь к authManager можно получить доступ через \Yii::$app->authManager.

По умолчанию yii\rbac\PhpManager хранит данные RBAC в файлах в каталоге @app/rbac. Убедитесь, что каталог и все файлы в нем имеют права записи для процесса веб-сервера, если нужно изменять иерархию прав в режиме реального времени.

Использование DbManager

Следующий код демонстрирует настройку authManager в конфигурации приложения с помощью класса yii\rbac\DbManager:

return [
    // ...
    'components' => [
        'authManager' => [
            'class' => 'yii\rbac\DbManager',
        ],
        // ...
    ],
];

Примечание: Если вы используете шаблон yii2-basic-app, в файле конфигурации config/console.php необходимо дополнительно объявить authManager, помимо config/web.php. В случае yii2-advanced-app authManager следует объявить только один раз в common/config/main.php.

DbManager использует четыре таблицы базы данных для хранения данных:

  • itemTable: таблица для хранения элементов авторизации. По умолчанию «auth_item».
  • itemChildTable: таблица для хранения иерархии элементов авторизации. По умолчанию «auth_item_child».
  • assignmentTable: таблица для хранения назначений элементов авторизации. По умолчанию «auth_assignment».
  • ruleTable: таблица для хранения правил. По умолчанию «auth_rule».

Перед продолжением необходимо создать эти таблицы в базе данных. Для этого можно использовать миграцию, хранящуюся в @yii/rbac/migrations:

yii migrate --migrationPath=@yii/rbac/migrations

Дополнительную информацию о работе с миграциями из разных пространств имен см. в разделе Разделенные миграции.

Теперь к authManager можно получить доступ через \Yii::$app->authManager.

Построение данных авторизации

Построение данных авторизации включает следующие задачи:

  • определение ролей и разрешений;
  • установление отношений между ролями и разрешениями;
  • определение правил;
  • связывание правил с ролями и разрешениями;
  • назначение ролей пользователям.

В зависимости от требований гибкости авторизации, задачи выше могут выполняться различными способами. Если вашу иерархию разрешений должны изменять только разработчики, вы можете использовать миграции или консольную команду. Преимущество миграций заключается в том, что они могут выполняться вместе с другими миграциями. Преимущество консольной команды заключается в том, что у вас есть хороший обзор иерархии в коде, а не разбросанности её по нескольким миграциям.

В любом случае, в итоге вы получите следующую иерархию RBAC:

Simple RBAC hierarchy

В случае необходимости динамического формирования иерархии разрешений вам понадобится пользовательский интерфейс или консольная команда. Сам API, используемый для построения иерархии, не будет отличаться.

Использование миграций

Вы можете использовать миграции для инициализации и изменения иерархии с помощью API, предоставляемых authManager.

Создайте новую миграцию с помощью ./yii migrate/create init_rbac, а затем реализуйте создание иерархии:

<?php
use yii\db\Migration;

class m170124_084304_init_rbac extends Migration
{
    public function up()
    {
        $auth = Yii::$app->authManager;

        // add "createPost" permission
        $createPost = $auth->createPermission('createPost');
        $createPost->description = 'Create a post';
        $auth->add($createPost);

        // add "updatePost" permission
        $updatePost = $auth->createPermission('updatePost');
        $updatePost->description = 'Update post';
        $auth->add($updatePost);

        // add "author" role and give this role the "createPost" permission
        $author = $auth->createRole('author');
        $auth->add($author);
        $auth->addChild($author, $createPost);

        // add "admin" role and give this role the "updatePost" permission
        // as well as the permissions of the "author" role
        $admin = $auth->createRole('admin');
        $auth->add($admin);
        $auth->addChild($admin, $updatePost);
        $auth->addChild($admin, $author);

        // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId()
        // usually implemented in your User model.
        $auth->assign($author, 2);
        $auth->assign($admin, 1);
    }
    
    public function down()
    {
        $auth = Yii::$app->authManager;

        $auth->removeAll();
    }
}

Если вы не хотите жестко задавать, у каких пользователей есть определенные роли, не помещайте вызовы ->assign() в миграции. Вместо этого создайте пользовательский интерфейс или консольную команду для управления назначениями.

Миграцию можно применить, используя yii migrate.

Использование консольной команды

Если ваша иерархия разрешений не меняется вообще и у вас есть фиксированное количество пользователей, вы можете создать -консольную команду, которая инициализирует данные авторизации один раз через API, предоставляемые authManager:

<?php
namespace app\commands;

use Yii;
use yii\console\Controller;

class RbacController extends Controller
{
    public function actionInit()
    {
        $auth = Yii::$app->authManager;
        $auth->removeAll();
        
        // add "createPost" permission
        $createPost = $auth->createPermission('createPost');
        $createPost->description = 'Create a post';
        $auth->add($createPost);

        // add "updatePost" permission
        $updatePost = $auth->createPermission('updatePost');
        $updatePost->description = 'Update post';
        $auth->add($updatePost);

        // add "author" role and give this role the "createPost" permission
        $author = $auth->createRole('author');
        $auth->add($author);
        $auth->addChild($author, $createPost);

        // add "admin" role and give this role the "updatePost" permission
        // as well as the permissions of the "author" role
        $admin = $auth->createRole('admin');
        $auth->add($admin);
        $auth->addChild($admin, $updatePost);
        $auth->addChild($admin, $author);

        // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId()
        // usually implemented in your User model.
        $auth->assign($author, 2);
        $auth->assign($admin, 1);
    }
}

Примечание: Если вы используете расширенную шаблон, вам нужно поместить ваши RbacController в директорию console/controllers и изменить пространство имен на console\controllers.

Вышеуказанную команду можно выполнить из консоли следующим образом:

yii rbac/init

Если вы не хотите жестко задавать, у каких пользователей есть определенные роли, не помещайте вызовы ->assign() в команду. Вместо этого создайте либо пользовательский интерфейс, либо консольную команду для управления назначениями.

Назначение ролей пользователям

Автор может создавать публикации, администратор может обновлять публикации и выполнять все действия, которые может автор.

Если ваше приложение позволяет пользователям регистрироваться, вам необходимо назначить роли этим новым пользователям один раз. Например, для того, чтобы все зарегистрированные пользователи стали авторами в вашем расширенном шаблоне проекта, вам нужно изменить frontend\models\SignupForm::signup() следующим образом:

public function signup()
{
    if ($this->validate()) {
        $user = new User();
        $user->username = $this->username;
        $user->email = $this->email;
        $user->setPassword($this->password);
        $user->generateAuthKey();
        $user->save(false);

        // the following three lines were added:
        $auth = \Yii::$app->authManager;
        $authorRole = $auth->getRole('author');
        $auth->assign($authorRole, $user->getId());

        return $user;
    }

    return null;
}

Для приложений, которые требуют сложного управления доступом с динамически обновляемыми данными авторизации, могут потребоваться специальные пользовательские интерфейсы (например, панель администратора), разработанные с использованием API, предоставляемых authManager.

Использование правил

Как уже упоминалось, правила добавляют дополнительные ограничения к ролям и разрешениям. Правило — это класс, расширяющий yii\rbac\Rule. Он должен реализовывать метод execute(). В созданной ранее иерархии автор не может редактировать свою собственную публикацию. Давайте исправим это. Сначала нам нужно правило для проверки того, что пользователь является автором публикации:

namespace app\rbac;

use yii\rbac\Rule;
use app\models\Post;

/**
 * Checks if authorID matches user passed via params
 */
class AuthorRule extends Rule
{
    public $name = 'isAuthor';

    /**
     * @param string|int $user the user ID.
     * @param Item $item the role or permission that this rule is associated with
     * @param array $params parameters passed to ManagerInterface::checkAccess().
     * @return bool a value indicating whether the rule permits the role or permission it is associated with.
     */
    public function execute($user, $item, $params)
    {
        return isset($params['post']) ? $params['post']->createdBy == $user : false;
    }
}

Вышеуказанное правило проверяет, создана ли post пользователем $user. Мы создадим специальное разрешение updateOwnPost в команде, которую мы использовали ранее:

$auth = Yii::$app->authManager;

// add the rule
$rule = new \app\rbac\AuthorRule;
$auth->add($rule);

// add the "updateOwnPost" permission and associate the rule with it.
$updateOwnPost = $auth->createPermission('updateOwnPost');
$updateOwnPost->description = 'Update own post';
$updateOwnPost->ruleName = $rule->name;
$auth->add($updateOwnPost);

// "updateOwnPost" will be used from "updatePost"
$auth->addChild($updateOwnPost, $updatePost);

// allow "author" to update their own posts
$auth->addChild($author, $updateOwnPost);

Теперь у нас есть следующая иерархия:

RBAC hierarchy with a rule

Проверка доступа

С готовыми данными авторизации проверка доступа так же проста, как вызов метода yii\rbac\ManagerInterface::checkAccess(). Поскольку большая часть проверок доступа связана с текущим пользователем, для удобства Yii предоставляет сокращённый метод yii\web\User::can(), который можно использовать следующим образом:

if (\Yii::$app->user->can('createPost')) {
    // create post
}

Если текущий пользователь — это Jane с ID=1, мы начинаем с createPost и пытаемся добраться до Jane:

Access check

Для проверки возможности пользователя обновить публикацию, нам необходимо передать дополнительный параметр, необходимый AuthorRule , описанным ранее:

if (\Yii::$app->user->can('updatePost', ['post' => $post])) {
    // update post
}

Вот что произойдёт, если текущий пользователь — John:

Access check

Мы начинаем с updatePost и переходим через updateOwnPost. Для прохождения проверки доступа AuthorRule должно вернуть true из своего метода execute(). Метод получает свой $params из вызова метода can(), поэтому значение равно ['post' => $post]. Если всё в порядке, мы попадем к author, которое назначено John.

В случае с Jane это немного проще, поскольку она администратор:

Access check

В вашем контроллере есть несколько способов реализации авторизации. Если вам нужны точные разрешения, разделяющие доступ к добавлению и удалению, вам необходимо проверять доступ для каждого действия. Вы можете либо использовать вышеуказанное условие в каждом методе действия, либо использовать yii\filters\AccessControl:

public function behaviors()
{
    return [
        'access' => [
            'class' => AccessControl::className(),
            'rules' => [
                [
                    'allow' => true,
                    'actions' => ['index'],
                    'roles' => ['managePost'],
                ],
                [
                    'allow' => true,
                    'actions' => ['view'],
                    'roles' => ['viewPost'],
                ],
                [
                    'allow' => true,
                    'actions' => ['create'],
                    'roles' => ['createPost'],
                ],
                [
                    'allow' => true,
                    'actions' => ['update'],
                    'roles' => ['updatePost'],
                ],
                [
                    'allow' => true,
                    'actions' => ['delete'],
                    'roles' => ['deletePost'],
                ],
            ],
        ],
    ];
}

Если все операции CRUD управляются вместе, целесообразно использовать одно разрешение, например managePost, и проверить его в yii\web\Controller::beforeAction().

В приведенном выше примере параметры не передаются с указанными ролями для доступа к действию, но в случае разрешения updatePost нам необходимо передать параметр post для его правильной работы. Вы можете передать параметры в yii\web\User::can(), указав roleParams в правиле доступа:

[
    'allow' => true,
    'actions' => ['update'],
    'roles' => ['updatePost'],
    'roleParams' => function() {
        return ['post' => Post::findOne(Yii::$app->request->get('id'))];
    },
],

В приведенном выше примере roleParams — это замыкание, которое будет оцениваться при проверке правила доступа, поэтому модель будет загружена только при необходимости. Если создание параметров роли — простая операция, вы можете просто указать массив, как показано ниже:

[
    'allow' => true,
    'actions' => ['update'],
    'roles' => ['updatePost'],
    'roleParams' => ['postId' => Yii::$app->request->get('id')];
],

Использование по умолчанию ролей

Роль по умолчанию — это роль, которая неявно назначается всем пользователям. Вызов yii\rbac\ManagerInterface::assign() не требуется, и данные авторизации не содержат информацию об её назначении.

Роль по умолчанию обычно связана с правилом, которое определяет, применима ли роль к проверяемому пользователю.

Роли по умолчанию часто используются в приложениях, которые уже имеют какую-либо систему назначения ролей. Например, приложение может иметь столбец «группа» в таблице пользователей для представления группы привилегий, к которой принадлежит каждый пользователь. Если каждая группа привилегий может быть сопоставлена с ролью RBAC, вы можете использовать функцию роли по умолчанию для автоматического назначения каждой роли RBAC пользователю. Давайте рассмотрим пример, как это можно сделать.

Предположим, в таблице пользователей у вас есть столбец group, который использует 1 для представления группы администраторов и 2 для группы авторов. Вы планируете иметь две роли RBAC admin и author для представления разрешений для этих двух групп соответственно. Вы можете настроить данные RBAC следующим образом,

namespace app\rbac;

use Yii;
use yii\rbac\Rule;

/**
 * Checks if user group matches
 */
class UserGroupRule extends Rule
{
    public $name = 'userGroup';

    public function execute($user, $item, $params)
    {
        if (!Yii::$app->user->isGuest) {
            $group = Yii::$app->user->identity->group;
            if ($item->name === 'admin') {
                return $group == 1;
            } elseif ($item->name === 'author') {
                return $group == 1 || $group == 2;
            }
        }
        return false;
    }
}

$auth = Yii::$app->authManager;

$rule = new \app\rbac\UserGroupRule;
$auth->add($rule);

$author = $auth->createRole('author');
$author->ruleName = $rule->name;
$auth->add($author);
// ... add permissions as children of $author ...

$admin = $auth->createRole('admin');
$admin->ruleName = $rule->name;
$auth->add($admin);
$auth->addChild($admin, $author);
// ... add permissions as children of $admin ...

Обратите внимание, что в приведенном выше примере, поскольку «author» добавлен как дочерний элемент «admin», когда вы реализуете метод execute() класса правила, вам также необходимо соблюдать эту иерархию. Вот почему, когда имя роли «author», метод execute() вернёт true , если группа пользователя равна 1 или 2 (что означает, что пользователь принадлежит к группе «admin» или «author»).

Далее, настройте authManager, перечислив две роли в yii\rbac\BaseManager::$defaultRoles:

return [
    // ...
    'components' => [
        'authManager' => [
            'class' => 'yii\rbac\PhpManager',
            'defaultRoles' => ['admin', 'author'],
        ],
        // ...
    ],
];

Теперь, если вы выполните проверку доступа, обе роли admin и author будут проверены путём оценки правил, связанных с ними. Если правило возвращает true, это означает, что роль применима к текущему пользователю. Основываясь на реализации правила выше, это означает, что если значение group пользователя равно 1, роль admin будет применена к пользователю; а если значение group равно 2, роль author будет применена.

© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc-2.0/guide-security-authorization.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API