Совершенство достигнуто, не тогда, когда нечего добавить,
а когда нечего убрать.

Антуан де`Сент-Экзюпери

 

  1.      Определение класса идентификации
  2.      Вход и выход из системы
  3.      Вход в систему на основе файлов cookie
  4.      Фильтр контроля доступа
  5.      Обработка авторизации
  6.      Контроль доступа на основе ролей
  7.      Настройка диспетчера авторизации
  8.      Определение иерархии авторизации
  9.      Использование бизнес-правил

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

Yii имеет встроенную систему аутентификации / авторизации (auth), которая проста в использовании и может быть настроена для особых нужд.

Центральная часть в структуре Yii auth представляет собой предварительно объявленный компонент пользовательского приложения, который является объектом, реализующим интерфейс IWebUser. Пользовательский компонент представляет собой постоянную идентификационную информацию для текущего пользователя. Мы можем получить доступ к нему в любом месте, используя Yii :: app () -> user.

Используя пользовательский компонент, мы можем проверить, зарегистрирован ли пользователь или нет через CWebUser :: isGuest; мы можем войти в систему и выйти из системы; мы можем проверить, может ли пользователь выполнять определенные операции, вызывая CWebUser :: checkAccess; и мы также можем получить уникальный идентификатор и другую постоянную идентификационную информацию о пользователе. 

1. Определение класса идентификации

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

Мы определяем идентификационный класс, который содержит фактическую логику аутентификации. Класс идентификации должен реализовывать интерфейс IUserIdentity. Различные классы идентификации могут быть реализованы для разных подходов аутентификации (например, OpenID, LDAP, Twitter OAuth, Facebook Connect). Хорошим началом при написании собственной реализации является расширение CUserIdentity, которое является базовым классом для подхода к аутентификации с использованием имени пользователя и пароля.

Основная работа по определению класса идентификации - это реализация метода IUserIdentity :: authenticate. Это метод, используемый для инкапсуляции основных деталей подхода аутентификации. Класс идентификации может также объявлять дополнительную идентификационную информацию, которая должна быть постоянной во время сеанса пользователя.

Пример

В следующем примере мы используем класс идентификации, чтобы продемонстрировать использование подхода базы данных к аутентификации. Это типично для веб-приложений. Пользователь вводит свое имя пользователя и пароль в форме входа, а затем мы проверяем эти учетные данные, используя ActiveRecord, против пользовательской таблицы в базе данных. В этом единственном примере демонстрируется несколько вещей:

  1.      Реализация метода authenticate () для использования базы данных для проверки учетных данных.
  2.      Переопределение метода CUserIdentity :: getId () для возврата свойства _id, поскольку реализация по умолчанию возвращает имя пользователя в качестве идентификатора.
  3.      Использование метода setState () (CBaseUserIdentity :: setState) для демонстрации хранения другой информации, которую можно легко получить при последующих запросах.

 

class UserIdentity extends CUserIdentity
{
    private $_id;
    public function authenticate()
    {
        $record=User::model()->findByAttributes(array('username'=>$this->username));
        if($record===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if(!CPasswordHelper::verifyPassword($this->password,$record->password))
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->_id=$record->id;
            $this->setState('title', $record->title);
            $this->errorCode=self::ERROR_NONE;
        }
        return !$this->errorCode;
    }
 
    public function getId()
    {
        return $this->_id;
    }
}

Когда мы включим логин и выход из системы в следующем разделе, мы увидим, что мы передаем этот класс идентификации в метод входа для пользователя. Любая информация, которую мы храним в состоянии (путем вызова CBaseUserIdentity :: setState), будет передана CWebUser, которая, в свою очередь, сохранит их в постоянном хранилище, таком как сеанс. Затем эту информацию можно получить как свойства CWebUser. В нашем примере мы сохранили информацию о названии пользователя через $ this-> setState ('title', $ record-> title) ;. Как только мы завершим процесс входа в систему, мы можем получить информацию о названии текущего пользователя, просто используя Yii :: app () -> user-> title.

Info: По умолчанию CWebUser использует сеанс как постоянное хранилище для информации о пользователях. Если вход в систему на основе файлов cookie включен (путем установки значения CWebUser :: allowAutoLogin), информация о идентификаторе пользователя также может быть сохранена в файле cookie. Убедитесь, что вы не объявляете конфиденциальную информацию (например, пароль) постоянной.

Хранение паролей в базе данных 

Безопасное хранение паролей пользователей в базе данных требует некоторой осторожности. Злоумышленник, который украл вашу пользовательскую таблицу (или ее резервную копию), может восстанавливать пароли с использованием стандартных методов, если вы не защищаете их. В приведенном выше примере кода используется встроенный CPAwordHelper Yii для хеширования пароля и проверки его (начиная с версии 1.1.14). CPasswordHelper :: hashPassword возвращает хэши, которые очень сложно взломать.

2. Вход в систему и выход из системы

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

// Login a user with the provided username and password.
$identity=new UserIdentity($username,$password);
if($identity->authenticate())
    Yii::app()->user->login($identity);
else
    echo $identity->errorMessage;
......
// Logout the current user
Yii::app()->user->logout();

Здесь мы создаем новый объект UserIdentity и передаем его аутентификационные данные (то есть значения $ username и $ password, представленные пользователем) его конструктору. Затем мы просто вызываем метод authenticate (). В случае успеха мы передаем идентификационную информацию в метод CWebUser :: login, который будет хранить идентификационную информацию в постоянном хранилище (по умолчанию для PHP-сессии) для извлечения при последующих запросах. Если аутентификация завершается с ошибкой, мы можем допросить свойство errorMessage для получения дополнительной информации о том, почему это не удалось.

Независимо от того, прошел ли аутентификация пользователя, можно легко проверить всю программу, используя Yii :: app () -> user-> isGuest. Если для хранения идентификационной информации используется постоянное хранилище, такое как сеанс (по умолчанию) и / или куки-файл (см. Ниже), пользователь может оставаться включенным после последующих запросов. В этом случае нам не нужно использовать класс UserIdentity и весь процесс входа в систему по каждому запросу. Скорее CWebUser автоматически позаботится о загрузке идентификационной информации из этого постоянного хранилища и будет использовать его, чтобы определить, вернет ли Yii :: app () -> user-> isGuest true или false.

3. Вход на основе файлов cookie

По умолчанию пользователь будет выведен из системы после определенного периода бездействия, в зависимости от конфигурации сеанса. Чтобы изменить это поведение, мы можем установить для свойства allowAutoLogin пользовательского компонента значение true и передать параметр длительности методу CWebUser :: login. Затем пользователь будет оставаться включенным в течение указанной продолжительности, даже если он закрывает окно своего браузера. Обратите внимание: эта функция требует, чтобы браузер пользователя принимал файлы cookie.

// Keep the user logged in for 7 days.
// Make sure allowAutoLogin is set true for the user component.
Yii::app()->user->login($identity,3600*24*7);

Как мы уже упоминали выше, когда включена авторизация на основе файлов cookie, состояния, сохраненные через CBaseUserIdentity :: setState, будут сохранены в файле cookie. В следующий раз, когда пользователь войдет в систему, эти состояния будут считаны из файла cookie и доступны через Yii :: app () -> user.

Хотя Yii имеет меры по предотвращению вмешательства cookie штата на стороне клиента, мы настоятельно рекомендуем не хранить конфиденциальную информацию безопасности как состояния. Вместо этого эти данные должны быть восстановлены на стороне сервера путем чтения из некоторого постоянного хранилища на стороне сервера (например, базы данных).

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


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

Чтобы реализовать вышеупомянутую стратегию, нам необходимо переопределить следующие два метода:

4. Фильтр контроля доступа 

Фильтр контроля доступа - это предварительная схема авторизации, которая проверяет, может ли текущий пользователь выполнить запрошенное действие контроллера. Авторизация основана на имени пользователя, IP-адресе клиента и типах запросов. Он предоставляется как фильтр, называемый «accessControl».

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

Чтобы контролировать доступ к действиям в контроллере, мы устанавливаем фильтр контроля доступа, переопределяя CController :: filters (подробнее об установке фильтров см. В разделе «Фильтр»).

class PostController extends CController
{
    ......
    public function filters()
    {
        return array(
            'accessControl',
        );
    }
}

В вышесказанном мы указываем, что фильтр контроля доступа должен применяться к каждому действию PostController. Подробные правила авторизации, используемые фильтром, определяются переопределением CController :: accessRules в классе контроллера.

class PostController extends CController
{
    ......
    public function accessRules()
    {
        return array(
            array('deny',
                'actions'=>array('create', 'edit'),
                'users'=>array('?'),
            ),
            array('allow',
                'actions'=>array('delete'),
                'roles'=>array('admin'),
            ),
            array('deny',
                'actions'=>array('delete'),
                'users'=>array('*'),
            ),
        );
    }
}

Вышеупомянутый код определяет три правила, каждый из которых представлен как массив. Первый элемент массива - это «разрешить» или «отклонить», а другие пары имя-значение задают параметры шаблона правила. Правила, определенные выше, интерпретируются следующим образом: действия по созданию и редактированию не могут выполняться анонимными пользователями; действие удаления может быть выполнено пользователями с ролью администратора; и действие удаления не может быть выполнено кем-либо.

Правила доступа оцениваются один за другим в том порядке, в котором они указаны. Первое правило, которое соответствует текущему шаблону (например, имя пользователя, роли, IP-адрес клиента), определяет результат авторизации. Если это правило является правилом allow, действие может быть выполнено; если это правило запрета, действие не может быть выполнено; если ни одно из правил не соответствует контексту, действие все равно может быть выполнено.

Совет. Чтобы гарантировать, что действие не будет выполнено в определенных контекстах, полезно всегда указывать правило отказа всех в конце набора правил, например следующее:
return array(
    // ... other rules...
    // the following rule denies 'delete' action for all contexts
    array('deny',
        'actions'=>array('delete'),
    ),
);
Причина этого правила заключается в том, что если ни одно из правил не соответствует контексту, действие будет продолжать выполняться.

Правило доступа может соответствовать следующим параметрам контекста:

 5. Обработка результатов авторизации

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


При настройке свойства loginUrl можно указать относительный или абсолютный URL. Можно также предоставить массив, который будет использоваться для создания URL-адреса, вызвав CWebApplication :: createUrl. Первый элемент массива должен указать маршрут к действию контроллера входа, а остальные пары имя-значение - параметры GET. Например,

array(
    ......
    'components'=>array(
        'user'=>array(
            // this is actually the default value
            'loginUrl'=>array('site/login'),
        ),
    ),
)

Если браузер переадресован на страницу входа в систему и логин будет успешным, мы можем перенаправить браузер обратно на страницу, которая вызвала сбой авторизации. Как узнать URL-адрес этой страницы? Мы можем получить эту информацию из свойства returnUrl пользовательского компонента. Таким образом, мы можем выполнить следующее перенаправление:

 

Yii::app()->request->redirect(Yii::app()->user->returnUrl);

6. Контроль доступа на основе ролей

Управление доступом на основе ролей (RBAC) обеспечивает простой, но мощный централизованный контроль доступа. Более подробную информацию о сравнении RBAC с другими более традиционными схемами управления доступом см. В статье Wiki.

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

Обзор

Основополагающей концепцией RBAC Yii является элемент авторизации. Элемент авторизации - это разрешение на выполнение чего-либо (например, создание новых сообщений в блогах, управление пользователями). Согласно своей детализации и целевой аудитории, элементы авторизации могут быть классифицированы как операции, задачи и роли. Роль состоит из задач, задача состоит из операций, а операция - это разрешение, которое является атомарным. Например, у нас может быть система с ролью администратора, которая состоит из задачи пост-управления и задачи управления пользователями. Задача управления пользователями может состоять в создании пользователя, обновлении пользователя и удалении пользовательских операций. Для большей гибкости Yii также позволяет выполнять роль других ролей или операций, задача состоит из других задач и операция, состоящая из других операций.

Элемент авторизации уникально идентифицируется по его названию.

Элемент авторизации может быть связан с бизнес-правилом. Бизнес-правило - это фрагмент кода PHP, который будет выполняться при выполнении проверки доступа к элементу. Только тогда, когда выполнение возвращает true, будет ли у пользователя считаться разрешение, представленное элементом. Например, при определении операции updatePost мы хотели бы добавить бизнес-правило, которое проверяет, совпадает ли идентификатор пользователя с идентификатором автора сообщения, чтобы только сам автор мог иметь разрешение на обновление сообщения.

Используя элементы авторизации, мы можем создать иерархию авторизации. Элемент A является родительским элементом другого элемента B в иерархии, если A состоит из B (или говорят, что A наследует разрешения (ы), представленные B). Элемент может иметь несколько дочерних элементов, а также может иметь несколько родительских элементов. Следовательно, иерархия авторизации представляет собой график частичного порядка, а не дерево. В этой иерархии элементы роли размещаются на верхних уровнях, элементы операций на нижних уровнях, а элементы задачи находятся между ними.

Когда у нас есть иерархия авторизации, мы можем назначить роли в этой иерархии для пользователей приложений. Пользователь, назначенный ролью, будет иметь права, представленные этой ролью. Например, если мы назначим роль администратора пользователю, у него будут права администратора, которые включают управление сообщениями и управление пользователями (и соответствующие операции, такие как создание пользователя).

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

 

if(Yii::app()->user->checkAccess('deletePost'))
{
    // delete the post
}

7. Configuring Authorization Manager

Прежде чем мы перейдем к определению иерархии авторизации и проверим проверку доступа, нам нужно настроить компонент приложения authManager. Yii предоставляет два типа менеджеров авторизации: CPhpAuthManager и CDbAuthManager. Первый использует файл сценария PHP для хранения данных авторизации, а последний хранит данные авторизации в базе данных. Когда мы настраиваем компонент приложения authManager, нам нужно указать, какой класс компонента использовать и каковы начальные значения свойств для компонента. Например,

 

return array(
    'components'=>array(
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'sqlite:path/to/file.db',
        ),
        'authManager'=>array(
            'class'=>'CDbAuthManager',
            'connectionID'=>'db',
        ),
    ),
);

Затем мы можем получить доступ к компоненту приложения authManager с помощью Yii :: app () -> authManager.

8. Определение иерархии авторизации

Defining authorization hierarchy involves three steps: defining authorization items, establishing relationships between authorization items, and assigning roles to application users. The authManager application component provides a whole set of APIs to accomplish these tasks.

To define an authorization item, call one of the following methods, depending on the type of the item:

Once we have a set of authorization items, we can call the following methods to establish relationships between authorization items:

And finally, we call the following methods to assign role items to individual users:

Below we show an example about building an authorization hierarchy with the provided APIs:

 

$auth=Yii::app()->authManager;
 
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');
 
$bizRule='return Yii::app()->user->id==$params["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);
$task->addChild('updatePost');
 
$role=$auth->createRole('reader');
$role->addChild('readPost');
 
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
 
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
 
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
 
$auth->assign('reader','readerA');
$auth->assign('author','authorB');
$auth->assign('editor','editorC');
$auth->assign('admin','adminD');

После того, как мы установили эту иерархию, компонент authManager (например, CPhpAuthManager, CDbAuthManager) автоматически загрузит элементы авторизации. Поэтому нам нужно только один раз запустить вышеуказанный код, а НЕ для каждого запроса.

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

9. Использование бизнес-правил 

Когда мы определяем иерархию авторизации, мы можем связать роль, задачу или операцию с так называемым бизнес-правилом. Мы также можем связать бизнес-правило, когда мы назначаем роль пользователю. Бизнес-правило представляет собой фрагмент кода PHP, который выполняется при выполнении проверки доступа. Возвращаемое значение кода используется для определения того, относится ли роль или присвоение к текущему пользователю. В приведенном выше примере мы связали бизнес-правило с задачей updateOwnPost. В бизнес-правиле мы просто проверяем, совпадает ли текущий идентификатор пользователя с идентификатором автора указанной записи. Информация о сообщениях в массиве $ params предоставляется разработчиками при выполнении проверки доступа.

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

Чтобы выполнить проверку доступа, нам сначала нужно знать имя элемента авторизации. Например, чтобы проверить, может ли текущий пользователь создавать сообщение, мы проверили бы, есть ли у него разрешение, представленное операцией createPost. Затем мы вызываем CWebUser :: checkAccess для проверки доступа:

 

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

Если правило авторизации связано с бизнес-правилом, которое требует дополнительных параметров, мы также можем передать их. Например, чтобы проверить, может ли пользователь обновлять сообщение, мы будем передавать данные post в параметрах $ params:

 

$params=array('post'=>$post);
if(Yii::app()->user->checkAccess('updateOwnPost',$params))
{
    // update post
}

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

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



Роль по умолчанию - это роль, которая неявно назначается каждому пользователю. Нам не нужно явно назначать его пользователю. Когда вызывается CWebUser :: checkAccess, сначала будут проверяться роли по умолчанию, как если бы они были назначены пользователю.

Роли по умолчанию должны быть объявлены в свойстве CAuthManager :: defaultRoles. Например, следующая конфигурация объявляет две роли в роли по умолчанию: authenticated и admin.

return array(
    'components'=>array(
        'authManager'=>array(
            'class'=>'CDbAuthManager',
            'defaultRoles'=>array('authenticated', 'admin'),
        ),
    ),
);

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

$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
 
$bizRule='return Yii::app()->user->name === "admin";';
$auth->createRole('admin', 'admin user', $bizRule);

Info: Начиная с версии 1.1.11 массив $ params, переданный бизнес-правилу, имеет ключ с именем userId, значение которого является идентификатором пользователя, для которого проверено бизнес-правило. Вам понадобится это, если вы вызываете CDbAuthManager :: checkAccess () или CPhpAuthManager :: checkAccess () в местах, где Yii :: app () -> пользователь недоступен, или нет пользователя, для которого вы проверяете доступ.