Как программировать с Yii2: элементы управления доступом к пользователю

10 января 2018

Если вы спросите, «Что такое Yii?», Ознакомьтесь с моим предыдущим учебным пособием: Введение в Yii Framework, в котором рассматриваются преимущества Yii и включает в себя обзор того, что новый в Yii 2.0, выпущенный в октябре 2014 года.

В этом программировании с сериями Yii2 я читаю читателей с использованием недавно обновленной Yii2 Framework для PHP.

В первой части мы локально установили Yii2, создали приложение Hello World, настроили удаленный сервер и использовали Github для развертывания нашего кода. Во второй части мы узнали о реализации Yii своей архитектуры Model View Controller и о том, как создавать веб-страницы и формы, которые собирают и проверяют данные.

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

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

Для этих примеров мы продолжим думать, что мы создаем структуру для публикации простых обновлений статуса, например. наш собственный мини-Twitter.

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

Что такое контроль доступа?

Контроль доступа интегрируется с функциями аутентификации фреймворка, чтобы разрешить или ограничить доступ к определенным функциям или страницам вашего веб-сайта.

Код, который мы написали до сих пор, позволяет кому-либо создавать записи, даже если они не вошли в систему. Например, в нашем примерном приложении вы можете посетить страницу состояния и опубликовать элементы без входа.

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

Yii2 также предлагает более продвинутый (и комплексный) контроль доступа на основе ролей (RBAC), который мы не будем внедрять в настоящее время. С RBAC вы определяете сложную иерархию разрешений для каждой возможной деятельности в вашем приложении.

Yii2 встроенный контроль доступа поддерживает только две роли по умолчанию: гость (не входит в систему), представленный '? ' И аутентифицированный, представленный '@ '. С помощью простых элементов управления доступом мы можем просто ограничить доступ к определенным страницам или действиям контроллера в зависимости от состояния входа. Если пользователи не вошли в систему при посещении страниц места, Yii перенаправит их на страницу входа.

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

Внедрение простых элементов управления доступом

В настоящее время наше приложение разрешает доступ к StatusController даже без входа. Пусть это исправлено.

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

. Здесь мы рассмотрим поведение доступа, но если вас это интересует, фильтры глаголов Yii позволяют вам ограничивать операции HTTP-запроса на основе действия вашего контроллера.

public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['post'],
                ],
            ],
            'access' => [
                        'class' => yiifiltersAccessControl::className(),
                        'only' => ['index','create','update','view'],
                        'rules' => [
                            // allow authenticated users
                            [
                                'allow' => true,
                                'roles' => ['@'],
                            ],
                            // everything else is denied
                        ],
                    ],           
        ];
    }

После добавления, если вы нажмете меню «Состояние», вы будете перенаправлены на страницу входа в систему:

Yii также обрабатывает перенаправление обратно на страницу индекса состояния после завершения входа в систему.

Добавление владельца модели

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

Yii:: $ app-> user-> getId ();

И мы можем связать сообщения статуса с их создателем в модели.

Чтобы сделать это, мы должны расширить таблицу состояния с новой миграцией таблицы:

./yii migrate/create extend_status_table_for_created_by
Yii Migration Tool (based on Yii v2.0.1)
Create new migration '/Users/Jeff/Sites/hello/migrations/m150128_003709_extend_status_table_for_created_by.php'? (yes|no) [no]:yes
New migration created successfully.

Вот код миграции, который добавляет столбец для created_by. Мы также добавляем внешний ключ для создания связи между полем Status-> created_by и таблицей User-> id.

<?php
use yiidbSchema;
use yiidbMigration;
class m150128_003709_extend_status_table_for_created_by extends Migratio
{
    public function up()
    {
      $tableOptions = null;
      if ($this->db->driverName === 'mysql') {
          $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=IoDB';
      }
      $this->addColumn('{{%status}}','created_by',Schema::TYPE_INTEGER.' NOT NULL');
      $this->addForeignKey('fk_status_created_by', '{{%status}}', 'created_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE');    
    }
    public function down()
    {
      $this->dropForeignKey('fk_status_created_by','{{%status}}');
      $this->dropColumn('{{%status}}','created_by');
    }
}

Пусть выполняется миграция:

./yii migrate/up
Yii Migration Tool (based on Yii v2.0.1)
Total 1 new migration to be applied:
    m150128_003709_extend_status_table_for_created_by
Apply the above migration? (yes|no) [no]:yes
*** applying m150128_003709_extend_status_table_for_created_by
    > add column created_by integer NOT NULL to table {{%status}} ... done (time: 0.009s)
    > add foreign key fk_status_created_by: {{%status}} (created_by) references {{%user}} (id) ... done (time: 0.007s)
*** applied m150128_003709_extend_status_table_for_created_by (time: 0.020s)
Migrated up successfully.

Я перезапустил генерацию кода Gii с новой таблицей состояния и скопировал и вставил новые дополнительные элементы. Большинство из них были незначительными, но вы заметите, что добавляет новый ActiveQuery для отношения:

/**
    * @return yiidbActiveQuery
    */
   public function getCreatedBy()
   {
       return $this->hasOne(User::className(), ['id' => 'created_by']);
   }

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

public function actionCreate()
    {
        $model = new Status();
        if ($model->load(Yii::$app->request->post())) {
          $model->created_by = Yii::$app->user->getId();
          $model->created_at = time();
          $model->updated_at = time();
           if ($model->save()) {            
             return $this->redirect(['view', 'id' => $model->id]);            
           }
        }
        return $this->render('create', [
            'model' => $model,
        ]);
    }

Мы также расширим виджет вида, включив в него поле created_by:

<?= DetailView::widget([
    'model' => $model,
    'attributes' => [
        'id',
        'message:ntext',
        'created_by',
        'permissions',
        'created_at',
        'updated_at',
    ],
]) ?>

Мы также можем использовать отношение created_by для отображения адреса электронной почты:

<?= DetailView::widget([
    'model' => $model,
    'attributes' => [
        'id',
        'createdBy.email',
        'message:ntext',
        'permissions',
        'created_at',
        'updated_at',
    ],
]) ?>

СозданнаяBy.email обращается к методу отношения Status:: getCreatedBy. Это отображает адрес электронной почты пользователя:

Однако, чтобы поддержать эту возможность с помощью расширения Yii2-пользователя, которое мы реализовали в четвертой части, нам пришлось внести две модификации. Во-первых, в нашем конфигурационном массиве app config web.php мы добавили переопределение модели в app models User.php:

...
'user' => [
            'class' => 'dektriumuserModule',
            'enableUnconfirmedLogin' => true,
            'confirmWithin' => 21600,
            'cost' => 12,
            'modelMap' => [
                    'User' => 'appmodelsUser',
              ],
            'admins' => ['admin']
        ],

Мы также создали эту модель в моделях app :

<?
amespace appmodels;
use dektriumusermodelsUser as BaseUser;
class User extends BaseUser
{
    public function register()
    {
        // do your magic
    }
}
?>

Эти два изменения обеспечили поддержку отношения.

Расширение простых элементов управления доступом

Что делать, если мы хотим иметь дополнительную возможность для действий контроллера? Например, что делать, если мы хотим ограничить операции удаления модераторами или администраторами? У простого управления доступом Yii2 нет модератора или концепции администратора, если вы не создадите его с помощью RBAC.

Обратите внимание, что расширение Yii2-user имеет идентификатор администратора для определенных пользователей, но также не хватает гибкости для дополнительных ролей.

Код Ninja написал хороший пример расширения простых элементов управления доступом для поддержки модераторов и администраторов (упрощенная авторизация на основе ролей в Yii 2.0), не прибегая к использованию RBAC. Их пример работает с расширенным шаблоном приложения Yii2.

Наше приложение отличается тем, что мы используем шаблон основного приложения Yii, и мы используем расширение Yii2-user. Поэтому я внес некоторые изменения в их руководство:

Во-первых, мы создаем каталог приложений компонентов и файл AccessRule.php, который расширяет встроенную модель AccessRule Yii:

<?php
amespace appcomponents;
use appmodelsUser;
class AccessRule extends yiifiltersAccessRule {
    /**
     * @inheritdoc
     */
    protected function matchRole($user)
    {
        if (empty($this->roles)) {
            return true;
        }
        foreach ($this->roles as $role) {
            if ($role == '?') {
                if ($user->getIsGuest()) {
                    return true;
                }
            } elseif ($role == User::ROLE_USER) {
                if (!$user->getIsGuest()) {
                    return true;
                }
            // Check if the user is logged in, and the roles match
            } elseif (!$user->getIsGuest() && $role == $user->identity->role) {
                return true;
            }
        }
        return false;
    }
}

Затем мы добавляем определения роли в наши приложения models User model:

<?
amespace appmodels;
use dektriumusermodelsUser as BaseUser;
class User extends BaseUser
{
  const ROLE_USER = 10;
  const ROLE_MODERATOR = 20;
  const ROLE_ADMIN = 30;

Пользователь Yii2 не создает столбец роли при создании новых пользователей. Таким образом, вы можете указать роли для модераторов и администраторов вручную в MySQL или позже создать собственный веб-интерфейс для предоставления ролей.

В вендоре dektrium yii2-user models RegistrationForm.php я добавил эту строку, чтобы определить роль пользователя для пользователей по умолчанию:

Примечание: вам придется внести это изменение вручную, если вы хочу, потому что каталог моего поставщика не попадает в наше дерево GitHub - и да, возможно, более элегантный способ сделать это в базовой кодовой базе после регистрации, например расширить метод createUser в моделях приложений User.php. Лучшей практикой может быть разветвление репозитория поставщика и внесение его в ваше собственное дерево кода.

**
     * Registers a new user account.
     * @return bool
     */
    public function register()
    {
        if ($this->validate()) {
            $user = $this->module->manager->createUser([
                'scenario' => 'register',
                'email'    => $this->email,
                'username' => $this->username,
                'password' => $this->password,
                'role'=>10, // User::ROLE_USER;               
            ]);
            return $user->register();
        }
        return false;
    }

Наконец, в StatusController.php мы добавляем некоторые библиотеки и эти определения доступа. В приведенном ниже примере действия обновления ограничены модераторами, а действия с удалением ограничиваются администраторами.

<?php
amespace appcontrollers;
use Yii;
use appmodelsStatus;
use appmodelsStatusSearch;
use appmodelsUser;
use appcomponentsAccessRule;
use yiiwebController;
use yiiwebNotFoundHttpException;
use yiifiltersVerbFilter;
use yiifiltersAccessControl;
/**
 * StatusController implements the CRUD actions for Status model.
 */
class StatusController extends Controller
{
   public function behaviors()
       {
           return [
               'verbs' => [
                   'class' => VerbFilter::className(),
                   'actions' => [
                       'delete' => ['post'],
                   ],
               ],
               'access' => [
                   'class' => AccessControl::className(),
                   // We will override the default rule config with the new AccessRule class
                   'ruleConfig' => [
                       'class' => AccessRule::className(),
                   ],
                   'only' => ['index','create', 'update', 'delete'],
                   'rules' => [
                       [
                           'actions' => ['index','create'],
                           'allow' => true,
                           // Allow users, moderators and admins to create
                           'roles' => [
                               User::ROLE_USER,
                               User::ROLE_MODERATOR,
                               User::ROLE_ADMI
                           ],
                       ],
                       [
                           'actions' => ['update'],
                           'allow' => true,
                           // Allow moderators and admins to update
                           'roles' => [
                               User::ROLE_MODERATOR,
                               User::ROLE_ADMI
                           ],
                       ],
                       [
                           'actions' => ['delete'],
                           'allow' => true,
                           // Allow admins to delete
                           'roles' => [
                               User::ROLE_ADMI
                           ],
                       ],
                   ],
               ],           
           ];
       }

Теперь, когда вы выходите из системы и посещаете страницу состояния на панели навигации, вы попадаете на экран входа в систему:

Когда вы входите в систему, вас снова отправят на индексный просмотр:

Однако, если я нажму «Удалить», я получу эту запретную правку доступа, потому что я нехороший пользователь, а не администратор:

Если вы хотите поднять себя до администратора, вы можете сделать это в базе данных, изменение столбца роли таблицы пользователя для вашего user_id до 20 для модератора и 30 для администратора. Повторите действия по обновлению и удалению, и вы будете разрешены в зависимости от выбранной вами роли.

Контроль доступа - одна из многих функций, которые делают меня огромным сторонником использования Yii Framework. Yii делает меня намного более эффективным разработчиком, способным предлагать решения гораздо быстрее, чем я могу с помощью ванильного PHP.

Что дальше?

Следите за предстоящими учебниками в своем программировании с помощью серии Yii2, когда я продолжаю погружаться в разные аспекты структуры. Вы также можете проверить мое строительство вашего запуска с помощью серии PHP, в которой используется расширенный шаблон Yii2, когда я создаю приложение реального мира.

Если вам захочется узнать, когда придет следующий учебник Yii2, следуйте за мной @reifman в Twitter или проверьте мою страницу инструктора. Моя страница инструктора будет включать все статьи из этой серии, как только они будут опубликованы. Вы также можете написать мне по электронной почте на моем веб-сайте Lookahead Consulting.

Ссылки по теме

Yii Веб-сайт Framework Введение в Yii Framework (Tuts +) Yii2 Developer Exchange, мой сайт ресурсов Yii2