Аутентификация, авторизация и RBAC в Yii2

Много времени прошло с публикации прошлой статьи про структуру ролей и разрешений. В этот раз порассуждали о понятиях и реализациях различных подходов к авторизации, аутентификации и контроле доступа на основе ролей в Yii2. Рассмотрели нюансы, сравнили друг с другом различные подходы к реализации RBAC:

Открыть на YouTube

Структура вебинара (кликабельно):

И приглашаю на следующие видеоуроки:

И задавайте вопросы в комментариях. Заранее спасибо и до встречи в эфире!

Другие статьи

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

Часто встречаю вопрос о том, что же это за странные блоки комментариев постоянно генерируются в представлениях, в ActiveRecord-моделях и перед всеми методами в коде? Что они обозначают и зачем они нужны? Это какой-то особый синтаксис объявления переменных в PHP или что?

Самая объёмная тема среди предложенных завершена. Выкладываю исправленную и дополненную запись вебинара-скринкаста о тестировании c PHPUnit и Codeception. Добавлены и доработаны примеры кода, пункты про аннотации, фикстуры, анализ покрытия, Faker, про установку всего через Composer и другие нюансы.

Недавно в обратную связь поступил вопрос: куда пойти работать неопытному молодому программисту? А именно, продолжить искать заказы на фрилансе или всё-таки устроиться удалённо в крупную студию или другую компанию и работать там?

Комментарии

 

Павел

Ура, наконец-то случилось, как долго я его ждал. Большое спасибо Дмитрий!

Ответить

 

Sergey

Я тоже долго ждал именно этот урок! Круто!

Ответить

 

Михаил

Спасибо за видеоматериал Дмитрий!

Ответить

 

Артем

Огромное спасибо за Ваш дело

Ответить

 

Евгений Левачев

красавчик, жаль что так редко выходят уроки

Ответить

 

Алексей Сундуков

А в чем диаграмма нарисована?

Ответить

 

Дмитрий Елисеев

В XMind.

Ответить

 

Александр Хуторненко

У Вас тут так подробно прописана система авторизации, может подскажете как сделать централизованную авторизацию для двух сайтов к примеру site.com и mail.site.com. site.com - сделан на yii2

Ответить

 

Дмитрий Елисеев

Можно сделать, например, общие cookies, указав site.com в качестве cookie domain.

Ответить

 

Александр Хуторненко

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

Ответить

 

Дмитрий Елисеев

Да, если делать на yii2-app-advanced.

Ответить

 

Олег

Дмитрий, когда можно будет почитать статейку с доработкой SEO service on Yii2 Framework?

С RABC вполне разобрался, ничего сложного, но Ваш блог очень интересен, многому научился именно от Вас.

Ответить

 

xfg

$autoRenewCookie это помоему не для того, чтобы куки было сложнее взломать. А чтобы при каждом запросе продлевать время жизни куки. То есть если время жизни куки установлено на 7 дней и $autoRenewCookie = false, то через 7 дней мы гарантировано будем разлогинены. А если true, то если в течении 7 дней были заходы на сайт, то аутентификация не слетит, так как мы своими заходами продлили этой куке жизнь.

Ответить

 

Дмитрий Елисеев

Для осложнения взлома служит authKey.

Ответить

 

Альберт Хасанов

Дмитрий подскажите тему следующего вебинара.

Ответить

 

Никита

Дмитрий, может Вы подскажите хорошие уроки по yii. Хочу начать сначала, а потом уже перейти на yii2

Ответить

 

Дмитрий Елисеев

Официальное руководство и рецепты на yiiframework.ru, видеоканал Meteor Yii, Богдана Березанского и подобные с курсами на YouTube, мои статьи, loco.ru и прочие блоги.

Ответить

 

Дмитрий Елисеев

К вопросу о том, как форсированно разлогинивать пользователя на всех устройствах при смене его пароля администратором.

Включаем хранение сессий в базе, в таблицу сессий добавляем поле user_id и, собственно, туда вписываем:

'session' => [
    'class' => 'yii\web\DbSession',
    'writeCallback' => function () {
        return ['user_id' => Yii::app()->user->id],
    },
],

А в контроллере при смене пароля администратором чистим все сессии нужного пользователя:

$db->createCommand()
    ->delete(Yii::$app->session->sessionTable, ['user_id' => $id])
    ->execute(); 

и меняем authKey для инвалидации Cookies.

Родилось случайно здесь.

Ответить

 

Алексей

Дмитрий, а зачем хранить сессии в БД и отягощать систему доп. запросами?
Можно просто добавить поле для юзера (к примеру "force_logout") и при той же операции проставлять ему "true". Всё равно данные пользователя достаются регулярно. Будет на одно поле больше, зато на несколько запросов в базу меньше.

Ответить

 

Дмитрий Елисеев

А когда потом это поле вернуть в false?

Ответить

 

Алексей

afterLogout().
не искал, что там с getId() юзера при этом, но это уже дело техники (пробросить).
Что скажете? )

Ответить

 

Дмитрий Елисеев

А как это разлогинит все ваши сессии на всех компьютерах и смартфонах?

Ответить

 

Алексей

надо помозгововать)

Ответить

 

Попов Вася

А примеры с вебинара можете дать?

Ответить

 

Дмитрий

В видео Вы коснулись темы логина под другим пользователем. Подскажите, а если я выполнил логин под другим пользователем, этого пользователя выбьет из приложения? И существует ли в Yii2 защита от двойного логина? Буду весьма благодарен, если Вы осветите этот материал.
Спасибо

Ответить

 

Дмитрий Елисеев

Выбъет, так как переменная _identity всего одна, как и cookie с сессией.

Ответить

 

Павел Меркулов

Дмитрий, спасибо большое за познавательный урок!
Вопрос не по теме) Скажите пожалуйста какая тема стоит у вас в хроме, понравился стиль.

Ответить

 

Дмитрий Елисеев

Стандартная тема Ubuntu.

Ответить

 

Павел Меркулов

Спасибо!
Дмитрий столкнулся с проблемой при аутентификации по HttpBasicAuth

Это написал в котроллере:

public function behaviors()
{
	return [
		'authenticator' => [
			'class' => HttpBasicAuth::className(),
			'realm' => 'Enter login and pass!',
			'auth' => function($login, $password) {
                                // для примера 
				return null;
			}
		],
	];
}

Но стандартного окошка авторизации не выводится, вместо этого ошибка:

exception 'yii\web\UnauthorizedHttpException' with message 'You are requesting with an invalid credential'.

Дмитрий, скажите пожалуйста что я не так делаю?

Ответить

 

Дмитрий Елисеев

Посмотрите Request в debug-панели. Может уже данные передаются. И попробуйте в другом браузере.

Ответить

 

Павел Меркулов

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

Ответить

 

Павел Меркулов

Все, разобрался ) Через curl отправил.

Ответить

 

Vitalij

Скажите пожалуйста, в этом видео есть пример как организовать работу через rest сервис?

Ответить

 

Дмитрий Елисеев

Если обращаться к чужому сервису, то использовать любой HTTP-клиент вроде Guzzle. А если из своего сайта делать сервис, то да, подключить любой из тех трёх методов аутентификации.

Ответить

 

Vitalij

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

Ответить

 

Sergalas

Не подскажите от чего может возникнуть ошибка: Rule not found: group ?

Ответить

 

Дмитрий Елисеев

Из-за того, что не туда вписали или не туда сохранили.

Ответить

 

Sergalas

вы уже смотрели мой вопрос на yiiframework.ru вопрос в том что и куда я мог не то сохранить ?

Ответить

 

Дмитрий Елисеев

Смотрел.

Ответить

 

Sergalas

Спасибо большое мне уже дали ответ там с неймспейсами проблема была :)

Ответить

 

Виталий Еропкин

Дмитрий, посмотрел Ваши видео по RBAC, очень понравилось. На основе увиденного и изученного стал делать для работы Админку, ну и заодно изучить Yii2 с этой стороны.
Всё шло хорошо, пока не столкнулся с маааленькой проблемой, которую самостоятельно могу решить, но с "костылями. Проблема следующая:"
1.Yii2, DbManager, PostgreSQL.
2.В миграции сделал так

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

// добавляем разрешение "fullAccess"
$fullAccess = $auth->createPermission('fullAccess');
$fullAccess->description = 'Полный доступ к ресурсам';
$auth->add($fullAccess);

// добавляем роль "superadmin" и даём роли разрешение "fullAccess"
$superadmin = $auth->createRole('admin');
$superadmin->description = 'Администратор';
$superadmin->data = 'Администратор всего содержимого данной программы, с неограниченным доступом';
$auth->add($superadmin);
$auth->addChild($superadmin, $fullAccess);

// Назначение роли пользователю superadmin
$auth->assign($superadmin, 1);

3. Сразу заметил, что

$superadmin->data = 'Администратор всего содержимого данной программы, с неограниченным доступом';

записывается в БД как сериализованная строка

  
s:142:"Администратор всего содержимого данной программы, с неограниченным доступом";

4. При выводе на экран, вот это s:142: не исчезает. Вывожу обычным DetailView::widget()

5. Сделал так:

[
     'label' => 'Подробное описание',
     'value' => unserialize($model->data),
],

не помогло.

6. С

<?php echo $form->field($model, 'data')->textarea(['rows' => 2])?>

та же проблема.

7. Нашёл костыль и записал в верху контроллера эту строку

iconv_set_encoding('input_encoding', 'UTF-8');

Помогло, unserialize заработал, но это костыль((( Можно как-то лучше сделать? Ошибка похоже банальная и где-то на поверхности , но не могу понять какая.

Основной вопрос по RBAC:
1. Как лучше апдейтить RBAC данные в БД?
2. По стандарту вот так

$model = $this->findModel($id);   // $model  модель AuthItem (по таблице Auth_Item) созданная RBAC
if ($model = load(Yii::$app->request->post()) && $model->save()) { .....}

Поле "data" не сериализуется, мне её самому сериализовать? или есть методы поближе к
authmanager() ?

Ответить

 

Дмитрий Елисеев

C использованием поля data для хранения описания не втречался. Обычно хватает description. А насчёт сериализации - увы, в Yii нет настраиваемого преобразования значений из базы и обратно, так что чаще всего все костылят это в методах afterFind (или instantiate) и beforeSave.

Ответить

 

Виталий Еропкин

т.е. если я напишу методы afterFind и beforeSave в модели AuthItem, то они будут работать послеПоиска и ПередСохранением?

Ответить

 

Дмитрий Елисеев

Да.

Ответить

 

Виталий Еропкин

Просто сделать beforeSave() не разрешил Yii2))) Говорит есть у меня такой метод, поэтому сделай как я хочу)) Сделал по "его"

public function beforeSave($insert)
{
    if (parent::beforeSave($insert)) {
        $this->data = serialize($this->data);
        return true;
    } else {
        return false;
    }
}
Ответить

 

Виталий Еропкин

Подскажите, чтобы такой фильтр написать https://yadi.sk/d/ZSH3Uf-Mnixun , только Yii2 методов хватит? или по-любому JS писать надо? Хочу научиться писать фильтры используя Yii2. Вот тут образец взял
http://rubkoff.ru/proekty/doma/iz-sruba?price1=on&area2=on&floor2=on&type3=on&view3=on
Сложный, но зато всё можно научиться для фильтров писать.

Ответить

 

Дмитрий Елисеев

Простые hidden-поля для формы и небольшой JS.

Ответить

 

Андрей Еськов

Дмитрий подскажите, к примеру, группе пользователей дано право удаление только своих контактов, как сделать проверку на уровне AccessControl

Вот так проверка работает отлично (во вьюхах и экшенах)


[
    'class' => 'yii\grid\ActionColumn',
    'template' => '{update}{delete}',
    'buttons' => [
        'update' => function ($url , $model) {
                return Yii::$app->user->can('update_Contact' , ['contact' => $model]) ? Html::a('<span class="btn btn-xs btn-white btn-info"><i class="fa fa-pencil"></i></span>', $url) : '';
            },
        'delete' => function ($url , $model) {
                return Yii::$app->user->can('delete_Contact'  , ['contact' => $model]) ? Html::a('<span class="btn btn-xs btn-white btn-danger"><i class="fa fa-trash"></i></span>', $url , [
                    'data' => [
                        'confirm' => 'Уверен что хочешь удалить контакт?',
                        'method' => 'post',
                    ]
                ]): '';
            },
    ],
    'headerOptions' => ['width' => '80px'],
],

Или просто открыть доступ до экшена удаления в AccessControl и в самом экшене уже проверять есть доступ, то велком нет доступа то форбитн.

Ответить

 

Дмитрий Елисеев

Да. чаще всего именно в экшенах и делают:

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    if (!Yii::$app->user->can('update_Contact' , ['contact' => $model])) {
        throw new ForbiddeHttpException();
    }
    ...
}
Ответить

 

Виталий Еропкин

Дмитрий. Вопрос опять по RBAC.
Не получается сделать по простому изменение полномочий пользователя. Сделал как-то грубо и много. Посмотрите на код, пожалуйста, это опять костыли? По проще ни как нельзя?

 
public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            $newrole = Yii::$app->request->post('Users')['roles'];
            $auth = Yii::$app->authManager;
            $oldrole = array_keys($auth->getRolesByUser($model->id_user))[0];
            $newrole = $auth->getRole($newrole);
            $oldrole = $auth->getRole($oldrole);
            $auth->revoke($oldrole, $model->id_user);
            $auth->assign($newrole , $model->id_user);
            return $this->redirect(['view', 'id' => $model->id_user]);
        } else {
            $model->roles = Yii::$app->authManager->getRoles();
            $role = Yii::$app->authManager->getRolesByUser($id);
            return $this->render('update', [
                'model' => $model, 'role' => $role,
            ]);
        }
    }
Ответить

 

Виталий Еропкин

Я тут хочу - Изменить "группу" (роль) у пользователя. У меня сделано так, что пользователь-(работник) входит в одну Группу в которой есть свои полномочия(разрешения). И как-то странно, что нет Update для assign().

Ответить

 

Дмитрий Елисеев

Удалить старую можно через revokeAll:

$auth = Yii::$app->authManager;
$auth->revokeAll($model->id_user);
$auth->assign($newrole , $model->id_user);
Ответить

 

Виталий Еропкин

Спасибо. Сегодня испытаю. Не думал, что All можно для одного))

Ответить

 

Виталий Еропкин
 $auth->assign($newrole , $model->id_user); 


только тут небольшая поправка.
$newrole должно быть объектом класса Role, поэтому исправил

 $auth->assign($auth->getRole($newrole) , $model->id_user); 

и всё работает.
Спасибо.

Ответить

 

Юрий

Добрый день!!
Возможно глупый вопрос но все же..
как разделить админов от пользователей в разные таблицы?
и как настроить страницу авторизации для пользователей и админов разные?

Ответить

 

Дмитрий Елисеев

А зачем их по таблицам разделять?

А так в app-advanced можно указать разные loginUrl и разные модели identityClass для компонента user.

Ответить

 

Юрий

Спасибо..
я так и понял..
да я это подсмотрел в opencart..

Ответить

 

Андрей

Скажите пожалуйста, каким способом можно ограничить доступ к методам стороннего модуля? Например yii2-admin

Ответить

 

Дмитрий Елисеев

Можно навесить поведение прямо к классу модуля:

'modules' => [
    'admin' => [
        'class' => '...',
        'as access' => [
            'class' => 'yii\filters\AccessControl',
            'rules' => [
                ...
            ],
        ],
    ],
],
Ответить

 

Андрей

Благодарю!
Если вас не затруднит, можно более подробно раскрыть данный вопрос?
Например я использую RBAC, в backend (админку) у меня имеют доступ 2 роли (roles): модератор и администратор и есть permission: enterSetupRbac, которая на данный момент есть только у администратора. Я хочу открыть функционал модуля yii-admin только для тех, у кого есть permission enterSetupRbac.
И в целом хотелось бы понять как можно гибко настраивать доступ к actions модулей на основе permissions.
Прошу прощения, если мои вопросы покажутся вам глупыми, я только начинаю изучать yii-2, до этого приходилось иметь дело только с kohana и CI.

Ответить

 

Дмитрий Елисеев

Если закрыть весь модуль, то:

'rules' => [
    [
        'allow' => true,
        'roles' => ['enterSetupRbac'],
    ],
],

Если конкретные контроллеры, то перечислить в actions:

'rules' => [
    [
        'controllers' => ['users'],
        'allow' => true,
        'roles' => ['usersView'],
    ],
],
Ответить

 

Андрей

Огромное спасибо!
Оказывается все просто.

Ответить

 

girmate

А если мне нужно в весь /backend ограничить доступ для всех кроме admin?

Ответить

 

Дмитрий Елисеев

В backend/config/main.php:

'as access' => [
    'class' => 'yii\filters\AccessControl',
    'except' => ['site/login', 'site/error'],
    'rules' => [
        [
            'allow' => true,
            'roles' => ['admin'],
        ],
    ],
],
Ответить

 

Филип

Роль гостя возможно создать через $authManager->createPermission('guest') и далее через Yii::$app->user->can() проверять доступ? Я так понимаю для гостей в Yii:$app->user->identity необходимо что-то хранить?

Ответить

 

Дмитрий Елисеев

Можно сделать createRole('guest') и прописать defaultRoles => ['guest']. А в Yii:$app->user->identity попадает только модель залогиненного пользователя.

Ответить

 

Филип

Благодарю, т.е. все кто не залоген будут иметь роль guest. Всех благ Вам и Вашему труду!

Ответить

 

Джони

Я не понят, от куда берется $post = new Post([///]) ?

Ответить

 

Mat

4 часа... Гигант !!!

Ответить

Оставить комментарий

Войти | Завести аккаунт


(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>