• Главная
  • Контакты
Подписаться:
  • Twitter
  • Facebook
  • RSS
  • VK
  • PushAll
logo

logo

  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • За год
    • Положительные
    • Отрицательные
  • Сортировка
    • По дате (возр)
    • По дате (убыв)
    • По рейтингу (возр)
    • По рейтингу (убыв)
    • По комментам (возр)
    • По комментам (убыв)
    • По просмотрам (возр)
    • По просмотрам (убыв)
Главная
  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • Главная
  • Yii2-advanced: Гибкая настройка Yii2 RBAC (роли, разрешения, правила)

Yii2-advanced: Гибкая настройка Yii2 RBAC (роли, разрешения, правила) +4

23.04.2017 10:36
Jekshmek 9 2200 Источник
Yii*, PHP*

У админа может и не быть доступа к разрешению пользователя и в пределах одной роли пользователи могут иметь разный доступ к разрешениям


Как организовать сущности Role,Permission,Rule


Роли (role): типовые роли supper_admin,admin,customer (сотрудник, менеджер),user (авторизированный пользователь),guest (не авторизированный пользователь). Роль supper_admin наследует от всех ролей разрешения благодаря этому supper_admin имеет доступ ко всем permission не зависимо от их наличия в конкретной роли но требуется пропуск во всех правилах;

Разрешения (permission): роль является прямым родителем разрешения, без наследования (кроме роли supper_admin).Другими словами, одно и тоже разрешение будет назначаться каждой нужной роли.
Правила (Rule): правила для ролей и для разрешений наследуются от BaseRole в котором присутствует проверка общих правил.

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

Админка для разрешений.
Добавление, удаление.

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

Подключение компонента:

 'components' => [
    ....
        'authManager' => [
            'class'           => 'yii\rbac\DbManager', 
            'itemTable'       => 'auth_item',
            'itemChildTable'  => 'auth_item_child',
            'assignmentTable' => 'auth_assignment',
            'ruleTable'       => 'auth_rule',
            'defaultRoles'    => ['guest'],// роль которая назначается всем пользователям по умолчанию
        ],
....]

Главное найти место в проекте где будет располагаться проверка, так как от этого зависят имена разрешений которые будут проверятся.

Самый простой способ — это сформировать ключи разрешений из action контроллера в который мы попадаем.Можно добавить в ключ админка это или фронтенд часть, название контроллера и метода и метода запроса GET,POST,PUT,DELETE… что бы сформировать уникальное название разрешения на всем сайте. К примеру fr_user_profile_get для фронтенда site.com.ua/user/profile методом GET

По ссылке ниже можно ознакомится с вариантами расположения проверки доступа:
Альтернативная настройка RBAC

Способ 1 — в методе контроллера
public function actionIndex()
{
    if (!\Yii::$app->user->can('index')) {
        throw new ForbiddenHttpException('Access denied');
    }
    return $this->render('index');
}


Способ 2 — прописать beforeAction
public function beforeAction($action)
{
    if (parent::beforeAction($action)) {
        if (!\Yii::$app->user->can($action->id)) {
            throw new ForbiddenHttpException('Access denied');
        }
        return true;
    } else {
        return false;
    }
}


Итак, вы уже определились с местом проверки разрешения.Теперь организуем логику.

Чтобы была возможность конкретному пользователю запретить определенное разрешение, мы создаем разрешение и именовануем названием разрешения с постпрефиксом _not которое при наличии у пользователя будет запрещать доступ, а проверка на наличие этого разрешения будет происходить в базовом правиле, от которого наследуются все правили для ролей и разрешений.

Проверка разрешения


/*
При проверки разрешения мы передаем массив параметров
конкретно в моем случае это класс который будет использован в правиле удаления/обновления
*/
if(!Yii::$app->user->can( 'ваш ключ разрешения',['class'=>static::class])){
       throw new \yii\web\ForbiddenHttpException('Access denied role ');
 }

Стартовое создание ролей и правил
  
 // RULES
                Yii::$app->authManager->removeAllRules();
               //общая проверка во всех разрешениях без правил на отсутствие блокирующего разрешения
                $BaseRule= new \common\rbac\BaseRule();
                Yii::$app->authManager->add($BaseRule);
                
               //только для разрешений
                $RuleUpdateDelete=new   \common\rbac\RuleUpdateDelete(); 
                Yii::$app->authManager->add($RuleUpdateDelete);

               // правило только для роли admin
                $RuleForAdmin= new \common\rbac\RuleForAdmin(); 
                Yii::$app->authManager->add($RuleForAdmin);

                // правило только для роли customer
                $RuleForCustomer= new \common\rbac\RuleForCustomer();   
                Yii::$app->authManager->add($RuleForCustomer);

               // правило только для роли user
                $RuleForUser= new \common\rbac\RuleForUser();
                Yii::$app->authManager->add($RuleForUser);

                // правило только для роли guest
                $RuleForGuest= new \common\rbac\RuleForGuest();
                Yii::$app->authManager->add($RuleForGuest);

// ROLES
                Yii::$app->authManager->removeAllRoles();

                $role_supper_admin = Yii::$app->authManager->createRole('supper_admin');
                $role_supper_admin->description='supper_admin';
                Yii::$app->authManager->add($role_supper_admin);

                $role_admin = Yii::$app->authManager->createRole('admin');
                $role_admin->description='Сотрудник admin';
                $role_admin->ruleName=$RuleForAdmin->name;
                Yii::$app->authManager->add($role_admin);

                $role_customer = Yii::$app->authManager->createRole('customer');
                $role_customer->description='Сотрудник customer';
                $role_customer->ruleName=$RuleForCustomer->name;
                Yii::$app->authManager->add($role_customer);

                $role_user = Yii::$app->authManager->createRole('user');// авторизирован
                $role_user->description='Авторизированный пользователь';
                $role_user->ruleName=$RuleForUser->name;
                Yii::$app->authManager->add($role_user);

                $role_guest = Yii::$app->authManager->createRole('guest');// не авторизирован
                $role_guest->description='Не авторизированный пользователь';
                $role_guest->ruleName=$RuleForGuest->name;
                Yii::$app->authManager->add($role_guest);

                //наследование тольку у суппер админа
                Yii::$app->authManager->addChild($role_supper_admin, $role_admin);
                Yii::$app->authManager->addChild($role_supper_admin, $role_customer);
                Yii::$app->authManager->addChild($role_supper_admin, $role_user);
                Yii::$app->authManager->addChild($role_supper_admin, $role_guest);


При создании роли учесть


create
 public function create(){
       // общая проверка во всех разрешениях без правил на отсутствие блокирующего разрешения 
        $BaseRule= new BaseRule();

        $role_new  = Yii::$app->authManager->createRole($this->role);
        $role_new->description=$this->description;
        if($this->data)$role_new->data=$this->data;
        // правило которое будет срабатывать при проверке на эту роль
        $role_new->ruleName=$BaseRule->name; 
        Yii::$app->authManager->add($role_new);

       // Добавление разрешений
        if($role_new=Yii::$app->authManager->getRole($this->role)){
            if(isset($this->permissions)){
                foreach ($this->permissions as $permission=>$val){

                    $child= Yii::$app->authManager->getPermission($permission);
                     if($child instanceof  yii\rbac\Permission && 
                       Yii::$app->authManager->canAddChild($role_new, $child))
                      {
                        Yii::$app->authManager->addChild($role_new, $child);
                      }
                }
            }
// Обязательно дабавляем новую роль к supper_admin так как он не имеет своих непосредственных разрешений
            $role_supper_admin=Yii::$app->authManager->getRole('supper_admin');
            if(Yii::$app->authManager->canAddChild($role_supper_admin, $role_new)){
                Yii::$app->authManager->addChild($role_supper_admin, $role_new);
            }
            return true;
        }else{
            return false;
        }
    }


При создании разрешения учесть


create
   public function create() {
/*
После валидации и инициализации аттрибутов
мы имеем $this->permission название разрешения
из котого мы должны понять какое правило ему назначить 
базовое или для удаление/обновления которое также наследуется от базового.
   Правило для удаления и обновления должно по мимо проверки запрещающих 
разрешений еще проверять имеет ли конкретный пользователь к изменяемой информации отношение
(т.е. если пользователь этот комментарий создал то он может его и удалить или 
изменить но другой пользователь кроме supper_admin,admin,customer)
   В $permission->data можете сохранять полезню информацию о составляющих вашего ключа
что б при редактировании легче можно было найти источник его.
*/
        if(preg_match('#.*(Delete|Put)$#',   $this->method) ){
            $Rule=Yii::$app->authManager->getRule('RuleUpdateDelete');
        }else{
            $Rule=Yii::$app->authManager->getRule('BaseRule');
        }

        $permission = Yii::$app->authManager->createPermission($this->permission);
        $permission->description = $this->description;
        // правило которое будет срабатывать при проверке на это разрешение
        $permission->ruleName = $Rule->name;
        $permission->data = [....];// ваши вспомогательные данные
        Yii::$app->authManager->add($permission);

      //Создаем роль-разрешение с поспрефиксом _not
        $permission_not  = Yii::$app->authManager->createPermission($this->permission.'_not');
        $permission_not  ->description = 'Для закрытия разрешения '.$this->permission;
        Yii::$app->authManager->add($permission_not  );

//Добавил в таблицу gr_auth_item поле isnot показывающее тип permission
        return Yii::$app->db->createCommand("UPDATE `gr_auth_item` SET `isnot`= 1 WHERE type=2 AND  name=:name")
            ->bindValue(":name", $this->permission.'_not',PDO::PARAM_STR)
            ->execute();
    }


Базовое правило


BaseRule
/*
    Проверка на роль supper_admin и запрещающее разрешение,
    это требуется у всех ролей 
*/
class BaseRule  extends \yii\rbac\Rule
{
    public $name ='BaseRule';
    public function execute($user_id, $permission, $params)
    {
        if(Yii::$app->user->can('supper_admin') )return 1;
        // при налии блокирующего разрешения у пользователя
        if(Yii::$app->user->can($permission->name.'_not') )return false; 
        //Даже у роли admin и manager может быть блокирующее разрешение
        return true;
    }
}


Базовое правило роли


RuleForUser
//Правило для конкретной роли (присутствует у каждой роли кроме supper_admin)
//срабатывает при проверке на причастность к роли   ...->can('user')
// к примеру user
/*
   Обычная проверка на причастность к самой роли и базовая проверка от BaseRule
*/
class RuleForUser extends BaseRule
{
    public $name='RuleForUser' ;

    public function execute($user_id, $role, $params)
    {
        $parent= parent::execute($user_id, $role, $params);
        if($parent===1)return true;
        if($parent==false)return false;

        if(isset(Yii::$app->authManager->getRolesByUser($user_id)[$role->name]))return true;
        return  false;
    }
}


Правило требующее проверки на изменение


RuleUpdateDelete
/*
В нем мы также наследуемся от BaseRule
*/ 
class RuleUpdateDelete extends BaseRule
{
    public $name = 'RuleUpdateDelete' ;

    public function execute($user_id, $permission, $params)
    {
       // пропускаем базовые проверки
         $parent= parent::execute($user_id, $permission, $params);
        if($parent===1)return true;
        if($parent==false)return false;

       // пропускаем такие роли как admin и customer
        if(Yii::$app->user->can('admin') || Yii::$app->user->can('customer'))return true;

        if(isset($params['class'])  && method_exists($params['class'], 'can') ){
            // проверка принадлежности пользователя к изменяемому объекту
            if(method_exists($params['class'], 'can'))
            return $params['class']::can($user_id);
            else return false;
        }
        return false;
    }
}

Какую работу выполняет параметр $params в методе execute?
Когда мы выполняем проверку
if(!Yii::$app->user->can( 'ваш ключ разрешения',['class'=>static::class])){
       throw new \yii\web\ForbiddenHttpException('Access denied role ');
 }

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

Итог


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

good luck, Jekshmek
Поделиться с друзьями
-->

Комментарии (9)


  1. wispoz
    23.04.2017 13:53
    #10187196

    Хорошо, это все относится к проверке доступа к методам, а как быть с доступом к записям из базы: пример, есть пость который может редактировать админ и пользователь (админ все, пользователь только свое)?


    1. Jekshmek
      24.04.2017 00:22
      #10187824

      Второй параметр метода Yii::$app->user->can('role',[...]) в который вы передаете нужные вам для этого данные, должен порещать все вопросы связанные с причастностью пользователя.


  1. vtvz_ru
    23.04.2017 14:35
    #10187238
    +1

    Вот сколько уже читаю эти статьи про rbac, и все равно до меня всего доходит. Скажите мне, это правда все так сложно или просто со мной что-то не так? Мне почему-то кажется это все немного запутанным и «чрезмерно универсальным»


    1. Mi7teR
      24.04.2017 00:14
      #10187814
      +1

      на самом деле rbac прост как три копейки


  1. Samouvazhektra
    23.04.2017 15:52
    #10187302

    Для многих проектов полный рбак действительно избыточен и сложности в понимание часто вносят как раз туториалы по упрощению и оптимизации.
    Попытки нагородить мегауниверсальное ownRule — далеко не всегда хорошее решение. Как-только задача выйдет из стандартного CRUD пойдут костыли

    Дело в том что мы не можем назначить пользователю разрешение мы можем только роль назначить при чем даже и не одну.

    Что значит — не можем?
    assign и revoke методы принимают как роль, так и полномочие


    1. Jekshmek
      24.04.2017 00:18
      #10187820

      Спасибо большое! Я по документации писал, а там как раз методы assign и revoke первым параметром принимают Yii\rbac\Role. Переписал код и теперь даже проще стало, без велосипеда с ролями.


  1. GraneDirval
    24.04.2017 00:13
    #10187812

    В такой маленькой статье столько грамматических ошибок…


    1. Jekshmek
      24.04.2017 00:15
      #10187818

      Исправлю если еще найду ошибки! Маленькая, как написали «на самом деле rbac прост как три копейки»


  1. sspat
    24.04.2017 14:35
    #10188610
    +2

    supper admin — администратор ужина? У вас там кулинарный портал какой-то?)

МЕТКИ

  • Хабы
  • Теги

Yii

PHP

yii2 framework

RBAC

role

permission

СЕРВИСЫ
  • logo

    CloudLogs.ru - Облачное логирование

    • Храните логи вашего сервиса или приложения в облаке. Удобно просматривайте и анализируйте их.
Все публикации автора
  • Yii2-advanced: Гибкая настройка Yii2 RBAC (роли, разрешения, правила) +4

    • 23.04.2017 10:36

    Yii2-advanced: Делаем интернационализацию с источником в Redis +12

    • 21.12.2016 07:05

Подписка


ЛУЧШЕЕ

  • Сегодня
  • Вчера
  • Позавчера
05:28

Мне надоело искать ошибки глазами — я создал бесплатный аудитор для 1С +15

08:23

Новая эра: нагрузочное тестирование UI-микросервисов +13

08:02

Включаем EPA в FreeTDS и go-mssqldb: приключение на 5 минут +12

06:40

Как мы сделали автогенерацию документации для CI/CD из комментариев в коде +12

08:01

Короткий промпт ≠ дешёвый промпт: как оптимизация ломает prefix cache в LLM-агентах +11

07:51

Перспективы термоядерных энергетических реакторов: краткий патентный анализ +11

05:07

ИИ-слоп убивает онлайн-сообщества +11

07:00

Видели, как блогеры мажут магазинный чек термозащитой для волос и жарят его плойкой? +10

07:05

Вы неправильно пишете асинхронный Rust: .await там, где его не должно быть +9

07:58

Кто набрал сотни звёзд в Open Source СНГ? И как ваш проект может оказаться следующим +7

07:01

Smart Timber: измеряем лес смартфоном. Часть 2: Технические решения для полевых условий +7

07:42

MFA для VPN в UserGate NGFW: как поднять удаленный доступ для Windows и macOS +6

07:08

В России изготовлен СВЧ-интерферометр для научных экспериментов по измерению плотности плазмы в НИУ «МЭИ» +6

07:04

Кто будет вести секции INFOSTART TECH EVENT 2026 +6

08:00

Celestica DS6000 series: первая ласточка 1,6-терабитных сетей или массовый продукт? +5

07:01

Репортаж с Hi-Tech Building 2026: как выглядит рынок умного дома +5

08:12

Код как документация: как мы строим самодокументируемые витрины данных в Почте Mail +4

08:12

Код как документация: как мы строим самодокументируемые витрины данных в Почте Mail +4

08:00

Короткие видео вместо текстовых комментариев: как я не с того конца тестировал новый формат обратной связи +4

07:26

Автоматизируем посёлок ч. 2: светофор своими руками +4

02:56

Небесная Ось Зла +81

14:18

Локальные LLM в реальной работе: Gemma 4, Qwen 3.6 и Qwen Coder +66

07:16

Как мы автоматизировали свой поселок +64

09:01

Россия выбыла из лунной гонки. Что планируют США и Китай: база, ядерный реактор, спутники слежения +37

07:00

Как технически устроена DPI-фильтрация у российских провайдеров и как её детектировать: разбор open-source инструментов +37

14:05

Bad Apple через CSS: как заставить браузер страдать без единой строчки JavaScript +32

20:17

Как работают с памятью в игровых консолях +29

09:00

Дорожная карта домашнего мини-ПК в 2026: что развернуть, в каком порядке, и зачем — план апгрейда от инфраструктурщика +29

13:01

Лёгкий, доступный, настоящий Телекастер Squier Debut Collection +27

08:00

Как мы в Selectel строим S3-хранилища: от железа до приложения +26

05:02

Хантавирусный круиз у берегов Испании, а также целевая блокировка VPN на 92% +16

13:00

NPU в ноутбуках: что меняется для тех, кто закупает корпоративную технику +15

08:11

Биологи переписали генетический код живой клетки. Что из этого получилось? +13

07:47

Структуры данных на практике. Глава 15: Графы и их обход с эффективным использованием кэша +13

14:44

Найм не спас, Telegram подвёл, ИИ устроил бардак: как мы просели, вернули деньги клиентам, но в итоге собрали веб-сервис +12

08:10

Снимаем показания счётчика воды «Бетар» по RS-485 и «МИР» по BLE с помощью ESP32 +12

12:05

Почему ваш Go‑сервис ломается под 1000 RPS и как найти узкое место за полчаса +11

20:07

Запуск Vivado 2019.1 на Orange Pi 3 LTS  через QEMU +10

02:08

Локальный агент для диагностики инфраструктуры +10

12:39

Коммерческая тайна и промышленный шпионаж: вспоминаем кражу кода у Google +9

13:30

Беспилотные рабочие Чернобыля +71

08:05

Золото демосцены +65

09:01

Электронные лампы — удивительные долгожители +54

13:01

Японцы в ярости от пиратства: как разница в менталитете породила один из самых горячих споров года +38

11:50

How it's made. Карта Морзе +38

06:55

Прочитал свой геном на кухне и превратил мазок щеки в 30 гигабаз данных ДНК +36

08:00

Framework полностью переделала свой модульный ноутбук: что изменилось за 5 лет +35

17:45

Я сделал приложение за вечер без навыков программирования. Фиг там. Как я почти год делал игру с опытом и ИИ +32

08:16

Почему текст от LLM узнаётся за пять секунд: разбираю стилистические маркеры через архитектуру моделей +30

10:09

Я плохой программист, плохой менеджер и вообще недоэксперт в любой области +27

08:59

Как открыть гравитон. Некоторые идеи о квантовании гравитационных волн +21

14:34

В агентскую эпоху не все архитектуры кода одинаково полезны +19

16:05

ИИ-госуслуги в ОАЭ, Claude в Adobe и Ableton, ChatGPT в таблицах и AI Spotify от ElevenLabs +14

12:00

ИИ-серверы Majestic Labs Prometheus: 128 ТБ памяти в одном устройстве, которое заменяет целую стойку +14

14:13

«Эмбеддинги на примерах с собаками», или как работать с векторными представлениями: книги и руководства по теме +13

13:20

Снятся ли искусственному интеллекту цифровые овцы? +12

07:15

Как я спроектировал систему защиты от протечек воды, или аккумулятор с транзистором побеждают бога морей +12

11:30

Тайны трёхцветных кошек +11

07:10

Новая архитектура для агентов: как Intel и SambaNova разделили инференс между GPU, RDU и CPU +11

15:13

Как хранятся большие бинарные данные в БД: накладные расходы BLOB в Oracle и Large Object в Postgres +8

ОБСУЖДАЕМОЕ

  • Почему у синьора 500k, а капитала всё ещё нет -3

    • 232   23000

    Локальные LLM в реальной работе: Gemma 4, Qwen 3.6 и Qwen Coder +66

    • 148   26000

    Рабочая сила стареет и немного про ДМС +1

    • 129   19000

    Как мы автоматизировали свой поселок +64

    • 96   17000

    Дорожная карта домашнего мини-ПК в 2026: что развернуть, в каком порядке, и зачем — план апгрейда от инфраструктурщика +29

    • 90   13000

    Claude Code — полный гайд и обучение для новичков с нуля +7

    • 84   54000

    Как открыть гравитон. Некоторые идеи о квантовании гравитационных волн +21

    • 74   10000

    Как я спроектировал систему защиты от протечек воды, или аккумулятор с транзистором побеждают бога морей +12

    • 74   10000

    Японцы в ярости от пиратства: как разница в менталитете породила один из самых горячих споров года +38

    • 67   15000

    Слова, которых нет +2

    • 58   19000

    Небесная Ось Зла +81

    • 56   16000

    У вас уже есть 48 млн — и вы этого не замечаете -6

    • 45   9600

    Если вы плохо понимаете бесконечности — добро пожаловать в сингулярность +1

    • 39   12000

    Я сделал приложение за вечер без навыков программирования. Фиг там. Как я почти год делал игру с опытом и ИИ +32

    • 36   24000

    ИИ атакует «беловоротничковые» профессии. Чему учить детей? Профессия «на всю жизнь» больше не работает -1

    • 35   8100
  • Главная
  • Контакты
© 2026. Все публикации принадлежат авторам.