Main > Yii > Yii2 расширяем роли

Yii2 расширяем роли

15.11.2016 14 comments » Views: 4,176

Yii 2

Потребовалось мне добавить пару ролей в проект Yii2. Задача стояла распределить пользователей на несколько групп. Для этих целей можно использовать RBAC, однако это решение в данном случае было слишком избыточно - требовалось что-то гораздо проще. Т.к. такая задача возникает довольно часто, напишу как можно справиться с ней всего парой строчек кода..

Начнем с модели User. Данные в моем случае хранятся в бд, поэтому User унаследован от ActiveRecord и реализует IdentityInterface.

Добавляем в модель (файл app\models\User.php) необходимые роли и метод проверки ролей can:

Далее добавляем колонку role в таблицу user, использя для этого либо миграции, либо напрямую редактируя таблицу, в зависимости от этапа разработки.

Теперь, создаем новый фильтр AccessRule (файл app\components\AccessRule.php), который наследуется от базового и переопределяет метод match:

Здесь мы добавили в конец проверок, вызов метода can из модели User который мы определили ранее.

Вот и всё, теперь нам осталось в соответствующем контроллере, переопределить namespace AccessRule на наш и можно использовать правила фильтрации как и раньше, но уже с нашими ролями:

Как видите, задача решается довольно просто. Слава фреймворку Yii 2 и его разработчикам 😀

Author: | Rating: 4/5 | Tags: , ,

14 comments.

Write a comment
  1. Виктор Шурыгин Reply
    27.12.2017 в 7:44 pm
    --------------
    Вау! Да ВЫ просто ВОЛШЕБНИК))) спасли меня!!!!!
    --------------

    Еще вопрос: вместо конструкции

    if (Yii::$app->user->getIdentity()) { .... }

    можно ведь использовать так?! или не правильно? вроде работает..

    if (!Yii::$app->user->isGuest) { .... }

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

    И да, еще вопрос... если использовать yii basic и создать модули user и admin и для каждого модуля свой identity (model user) - то ваш код отказывается работать... начинает обращаться к identity который прописан в конфиге. А я хотел разделить регистрацию/авторизацию как например можно было сделать в yii1 - а вот как в yii2 так сделать? чтобы например создал 3 модуля админа, модератора, редактора.. и чтобы у каждого модуля был своя форма авторизации и регистрации?
    • Vitaliy Orlov Reply
      28.12.2017 в 3:25 pm
      Привет,

      Yii::$app->user->isGuest
      в коде выглядит так

      ...
      public function getIsGuest()
      {
      return $this->getIdentity() === null;
      }
      ...
      смотри тут https://github.com/yiisoft/yii2

      т.е. можно использовать и так, и так
      • Виктор Шурыгин Reply
        30.12.2017 в 3:05 am
        Спасибо, я все понял!
  2. Виктор Шурыгин Reply
    27.12.2017 в 6:15 pm
    Спасибо за такой простой хак. Есть вопрос: а как можно проверять во вьюшках? например чтобы выводить для каждой роли свою инфу...? Пробовал так
    --
    User::can(User::ROLE_USER) - но пишет что не известно свойство.

    а так вообще не видит метода
    -
    Yii::$app->user->identity->can(USER::ROLE_ADMIN);

    как мне по вашему способу ролей проверять эти роли во вьюшках?

    пробовал в модель User добавить геттеры и сеттеры, но не робит.

    public static function getRole(){
    return !empty($this->role) ? $this->role : "";
    }


    public static function setRole(){
    return $this->role = $role;
    }

    Подскажите пожалуйста?!
    • Vitaliy Orlov Reply
      27.12.2017 в 7:22 pm
      Привет, попробуй так во вьюшке:

      if (Yii::$app->user->getIdentity()) {
      if (Yii::$app->user->getIdentity()->can(\app\models\User::ROLE_USER)) {
      // разрешено
      }
      }
    • Виктор Шурыгин Reply
      27.12.2017 в 7:54 pm
      Почему такие извращения? дело в том,что у меня был самописный проект..и каждый тип пользователя ( админ, редактор, модератор) находятся в отдельных таблицах. На yii1 я быстро перешел... а вот на yii2 никак не могу решить проблему.. Как при авторизации понять, в какой таблице искать? Когда все пользователи в одной таблице находятся- это понятно... А вот если в разных? Может вопрос кажется глупым, но я уже мозг сломал свой..никак не получается реализовать. Разные identity почему то не работают... Как бы работают...авторизация проходит..но вот потом почему то подключается identity который указан в конфиге.... Брр... сумбур уже в голове. А так хочется перелезть с yii1 на yii2
      • Виктор Шурыгин Reply
        27.12.2017 в 10:10 pm
        спасибо за помощь! я решил проблему))) оказалось намного все проще с вашим приведенным в статье кодом))) а я начал велосипед изобретать))
      • Vitaliy Orlov Reply
        28.12.2017 в 3:18 pm
        Если еще не знаешь, то на гитхабе в официальном репозитории есть перевод доков на русский, там неплохо все описано, в том числе роли (через RBAC): https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/security-authorization.md

        Identity класс не обязательно должен наследоваться от ActiveRecord, т.е. ты можешь сделать внутри него кастомную логику которая будет обращаться к разным таблицам. Кроме того, если там совсем все сложно, то можно вообще отказаться от Identity, определить свою модель аутентификации и использовать её, но это на крайний случай, т.к. любые не стандартные решения, в большинстве случаев затратны в плане дальнейшей поддержки.
        • Виктор Шурыгин Reply
          30.12.2017 в 3:07 am
          --- т.е. ты можешь сделать внутри него кастомную логику которая будет обращаться к разным таблицам --

          Вроде как понимаю, и с другой стороны все равно не пойму..... Краткий примерчик бы, чтоб глянуть.
        • Виктор Шурыгин Reply
          30.12.2017 в 3:13 am
          через RBAC не хочется, слишком замудренно. Для моего проекта не нужно такой функционал. Поэтому меня и заинтересовала твоя статья. Вот бы еще примерчик того, как с разных таблиц получать данные при авторизации.

          Я вот представляю так: что делаем свою логику , а уже потом просто вызвать метод компанента из коробки - Yii::$app->getUser()->login($user) ..... или не так? но опять же, все равно нужно наследовать identity -
        • Виктор Шурыгин Reply
          30.12.2017 в 3:17 am
          или создать модели для каждой талицы (админ, редактор, пользователь) и уже реализовать identity и работать с конкретной моделью? так?
          • Vitaliy Orlov
            30.12.2017 в 5:09 pm
            Когда у тебя 3и таблицы, то Identity должен понимать из какой таблицы (модели) ему брать информацию. У него есть обязательные методы:

            ..
            public function getId()
            public static function findIdentity($id)
            ..
            так вот getId у тебя должен возвращать уникальный id для 3х таблиц, а findIdentity находить нужный по id

            не проверял, но идея кодом будет выглядеть примерно так


            namespace app\models;
            class Admin extends ActiveRecord {
            ...
            }
            class Editor extends ActiveRecord {
            ...
            }

            namespace app\models;

            class User implements IdentityInterface
            {
            public $id;
            public $model;

            public static function findIdentity($id)
            {
            $ret = null;

            list($model, $id) = explode('::', $id);

            if (class_exists($model)) {
            $ret = new static;
            $ret->id = $id;
            $ret->model = $model;
            }

            return $ret;
            }

            public static function findIdentityByAccessToken($token, $type = null)
            {
            return null;
            }

            public function getId()
            {
            return $this->model.'::'.$this->id;
            }

            public function getAuthKey()
            {
            $model = $this->getModelInstance()->findOne($id);
            return $model ? $model->authKey : null;
            }

            public function validateAuthKey($authKey)
            {
            return $this->getAuthKey() === $authKey;
            }

            public function getModelInstance(){
            $model = $this->model;
            return new $model;
            }
            }
            Пример дальнейшего использования:

            // логин в контролере
            $identity = Yii::$app->findIdentity('\app\models\Admin::1');
            Yii::$app->user->login($identity);

            // вывод имени пользователя во вьюхе
            $identity = Yii::$app->user->getIdentity();
            if ($identity) {
            $identity->getModelInstance()->username;
            }
            Думаю, идея понятна, но надо проверять..

            Другой способ, это завести еще одну таблицу, например так:

            ======================
            табл user
            -------
            id: 1
            model: admin_user
            model_id:1
            -------
            id: 2
            model: editor_user
            model_id:1
            ======================
            табл admin_user
            -------
            id: 1
            username: Admin
            ======================
            и определить в классе user метод getModel, который будет брать запись из зависимой таблицы на основе полей model и model_id

            Еще один способ, отказаться вообще от компонента \yii\web\User и написать свой компонент аутентификации со своим блэкджеком и дальше использовать его.
        • андрей Reply
          06.04.2019 в 5:29 am
          не надо ляля, в дках ничего толком не описано и нет примеров. Запарили советовать всякую шляпу
          • Vitaliy Orlov
            07.04.2019 в 12:43 pm
            Да ладно :) просто ссылка на страницу доков поменялась (уже обновил), вот актуальная:
            https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/security-authorization.md

            Все там есть, с примерами, подробным объяснением и умными словами по которым можно погуглить.

Leave a Reply

Your email address will not be published. Required fields are marked *

Allowed HTML-tags: <a>, <code>, <i>, <em>, <strong>, <b>, <u>, <strike>