Идентификация, аутентификация и авторизация пользователей является обычной частью
почти каждого веб-приложения. В CakePHP AuthComponent предоставляет
подключаемый способ выполнения этих задач. AuthComponent позволяет комбинировать
объекты аутентификации и объекты авторизации для создания гибких
способов идентификации и проверки авторизации пользователя.
Аутентификация
Аутентификация - это процесс идентификации пользователей посредством
учетных данных и обеспечение того, чтобы пользователи были теми, за кого они
себя выдают. Как правило, это делается с помощью имени пользователя и пароля,
которые проверяются по списку известных пользователей. В CakePHP есть
несколько встроенных способов аутентификации пользователей, имеющихся у вашего
приложения.
FormAuthenticate
позволяет аутентификацию пользователей на основании
POST-данных, отправляемых с помощью форм. Как правило, это форма входа с
возможностью ввода учетных данных пользователем.
BasicAuthenticate
предоставляет возможности Базовой HTTP-аутентификации.
DigestAuthenticate
предоставляет возможности Дайджест-аутентификации.
По умолчанию AuthComponent
использует FormAuthenticate
.
Выбор типа аутентификации
Как правило, вы захотите предложить аутентификацию на основе форм. Это самый
простой способ для пользователей веб-браузеров. Если вы создаете API или веб-сервис,
вы можете рассмотреть базовую аутентификацию или дайджест-аутентификацию. Основные
различия между дайджест- и базовой аутентификацией в основном связаны с тем, как
обрабатываются пароли. При базовой аутентификации имя пользователя и пароль
передаются в виде обычного текста на сервер. Это делает базовую аутентификацию
непригодной для приложений без SSL, так как вы будете подвергать пароли уязвимости.
Дайджест-аутентификация передает в хэшированном виде имя пользователя, пароль и
некоторые другие детали. Это делает дайджест-аутентификацию более подходящей для
приложений без SSL-шифрования.
Вы также можете использовать такие системы аутентификации, как например OpenID,
но они уже не входят в состав ядра CakePHP.
Настройка обработчиков аутентификации
Вы настраиваете обработчики аутентификации, используя конфигурацию
authenticate
. Вы можете настроить один или несколько обработчиков
для аутентификации. Использование нескольких обработчиков позволяет
вам поддерживать разные способы входа пользователей. Когда пользователи
авторизуются, обработчики аутентификации проверяются в том порядке,
в котором они были объявлены. Как только какой-либо обработчик сможет
идентифицировать пользователя, все оставшиеся обработчики уже не будут
использованы. И наоборот, вы можете прервать проверку подлинности,
выбросив исключение. Вам будет нужно перехватывать любые выбрасываемые
исключения и обрабатывать их должным образом.
Вы можете настроить обработчики аутентификации в методе
beforeFilter()
либо initialize()
. Вы можете передавать
информацию о конфигурации в каждый объект аутентификации, используя
массив:
// Простая настройка
$this->Auth->config('authenticate', ['Form']);
// Передача параметров
$this->Auth->config('authenticate', [
'Basic' => ['userModel' => 'Members'],
'Form' => ['userModel' => 'Members']
]);
Во втором примере вы возможно обратили внимание, что ключ userModel
был объявлен дважды. Чтобы ваш код соответствовал принципам DRY (не
повторяйся), вы можете использовать ключ all
. Этот специальный ключ
позволяет вам устанавливать параметры, которые вы передаете к каждому
прикрепленному объекту. Ключ all
также доступен в качестве
статического свойства AuthComponent::ALL
:
// Передача параметров с помощию 'all'
$this->Auth->config('authenticate', [
AuthComponent::ALL => ['userModel' => 'Members'],
'Basic',
'Form'
]);
В приведенном выше примере и Form
и Basic
будут получать настройки,
объявленные в ключе „all“. Любые настройки, переданные конкретному объекту
аутентификации будут переопределять соответствующие ключи внутри ключа „all“.
Объекты аутентификации ядра поддерживают следующие ключи конфигурации.
fields
Поля, используемые для аутентификации пользователя. Вы можете
использовать ключи username
и password
, чтобы указать поля для
имени пользователя и пароля соответственно.
userModel
Имя модели для таблицы пользователей; По умолчанию - Users.
finder
Метод-файндер для получения записи из таблицы пользователей.
По умолчанию устновлен в „all“.
passwordHasher
Класс хешера паролей; По умолчанию Default
.
Опции scope
и contain
являются устаревшими в версии 3.1. Используйте
вместо них пользовательские файндеры для изменения запроса на выборку записи
пользователя.
Опция userFields
является устаревшей в версии 3.1. Используйте метод
select()
в вашем пользовательском файндере.
Чтобы настроить дополнительные поля для записи пользвателя в методе
initialize()
:
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email', 'password' => 'passwd']
]
]
]);
}
Не помещайте другие ключи конфигурации Auth
, такие как authError
, loginAction
,
и т.д. внутрь элементов authenticate
или Form
. Они должны находиться на одном
с ними уровне. Приведенная выше настройка конфигурации компонента Auth
с использованием
остальных параметров должна выглядеть так:
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => 'Users',
'action' => 'login',
'plugin' => 'Users'
],
'authError' => 'Вы правда думали, что вам можно видеть это?',
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email']
]
],
'storage' => 'Session'
]);
}
В дополнение к стандартной конфигурации, Базовая аутентификация (Basic)
также поддерживает следующие ключи:
В дополнение к стандартной конфигурации, Дайджест-аутентификация
также поддерживает следующие ключи:
realm
Область, для которой предназначена аутентификация.
По умолчанию servername (имя сервера).
nonce
Значение nonce для аутентификации. По умолчанию uniqid()
.
qop
По умолчанию auth; другие значения пока не поддерживаются.
opaque
Строка, которая должна быть возвращена в неизменном виде
клиентами. По умолчанию md5($config['realm'])
.
Примечание
Чтобы найти запись пользователя, запрос к базе данных происходит только
с использованием имени пользователя. Проверка пароля производится в PHP.
Это связано с тем, что алгоритмы хеширования, такие как bcrypt (алгоритм
по умолчанию) генерируют новый хеш каждый раз, даже для неизменной строки,
и в данном случае обычное сравнение строк в SQL становится неприменимым
для проверки пароля.
Кастомизация поискового запроса
Вы можете кастомизировать запрос на выборку записи пользователя с помощью
опции finder
в группе параметров authenticate
:
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth'
]
],
]);
}
Это потребует наличия поискового метода findAuth()
в вашем классе модели
UsersTable
. В приведенном ниже примере запрос скорректирован для выборки
значений только из необходимых полей и добавлено условие выборки значений.
Вы должны убедиться, что происходит выборка значений из необходимых полей,
таких как username
и password
:
public function findAuth(\Cake\ORM\Query $query, array $options)
{
$query
->select(['id', 'username', 'password'])
->where(['Users.active' => 1]);
return $query;
}
Примечание
Опция finder
доступна только с версии 3.1. В более ранних версиях вы
можете использовать опции scope
и contain
для изменения запроса.
Идентификация и вход пользователей
-
AuthComponent::identify()
Вам необходимо вручную вызывать $this->Auth->identify()
, чтобы
идентифицировать пользователя, используя учетные данные предоставленные
в запросе. После этого вы должны использовать метод $this->Auth->setUser()
,
чтобы пользователь вошел в приложение, то есть данные о нем сохранились в
сессии.
При аутентификации пользователей прикрепленные объекты аутентификации
проверяются в том порядке, в котором они прикреплены. Как только один из
объектов сможет идентифицировать пользователя, другие объекты уже не проверяются.
Пример функции для работы с формой входа может выглядеть так:
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
} else {
$this->Flash->error(__('Username or password is incorrect'));
}
}
}
Приведенный выше код сначала попробует идентифицировать пользователя, используя
POST-данные. В случае успеха данные о пользователе будут сохранены в сессии, благодаря
чему будут доступны между отправкой запросов, и после этого будет осуществляться
перенаправление на последнюю посещенную страницу, либо на URL, указанный в параметре
конфигурации loginRedirect
. В случае, если попытка входа окажется неудачной -
выведется флеш-сообщение об ошибке.
Предупреждение
Метод $this->Auth->setUser($data)
авторизует пользователя, независимо от того,
какие данные были ему переданы. Он не будет проверять пользовательские данные на
соответствие классу аутентификации.
Перенаправление пользователей после входа
-
AuthComponent::redirectUrl()
После входа пользователя в систему вы, как правило, захотите перенаправить их
обратно туда, откуда они пришли. Передайте URL-адрес для установки целевой
страницы, на которую пользователь должен быть перенаправлен после входа в
систему.
Если параметр не будет передан, возвращаемый URL будет подчиняться следующим
правилам:
Возвращается нормализованный URL из значения redirect
строки запроса если
он существует и находится в тод же домене, что и текущее приложение. До версии
3.4.0 использовалось значение сессионной переменной Auth.redirect
.
Если в строке запроса/сессии нужное значение отсутствует, но присвоено
какое-либо значение параметру конфигурации loginRedirect
, то будет
возвращено это значение.
Если же и в параметре loginRedirect
не окажется нужного значения, будет
возвращен /
.
Создание системы аутентификации без сохранения состояния
Базовая и Дайджест-аутентификация - это системы аутентификации не сохраняющие
состояние, и не требующие исходных POST-данных или формы. Если вы используете
только эти два способа аутентификации, вашему котроллеру необязательно наличие
экшена входа в систему (login). Cистема аутентификации без сохранения состояния
перепроверяет данные пользователя при каждом запросе. это создает небольшое
количество дополнительных накладных расходов, но позволяет клиентам
осуществлять вход без использования куки и делает AuthComponent
более гибким
при создании API.
Для аутентификаторов без сохранения состояния параметр конфигурации storage
следует установить в Memory
, чтобы AuthComponent
не использовал сеccию
для хранения записи пользователя. Вы также можете настроить параметр конфигурации
unauthorizedRedirect
в false
, чтобы AuthComponent
выбрасывал
ForbiddenException
вместо поведения по умолчанию - перенаправления на ссылающуюся
страницу.
- ..note::
Параметр unauthorizedRedirect
действует только для аутентифицированных
пользователей. Для обработки неаутентифицированного доступа без перенаправления
вы должны загрузить вам понадобится загрузить один или несколько аутентификаторов
без сохранения состояния, таких как Базовый или Дайджест. В противном случае ваш
запрос будет перенаправлен на loginAction
.
Объекты аутентификации могут реализовывать метод getUser()
, который может
использоваться для поддержки систем входа пользователя, независящих от файлов
cookie. Типичный метод getUser()
рассматривает запрос/среду и использует эту
информацию для подтверждения личности пользователя. Например, Базовая HTTP-аутентификация
использует $_SERVER['PHP_AUTH_USER']
и $_SERVER['PHP_AUTH_PW']
для полей
имени пользователя и пароля.
Примечание
Если аутентификация не работает как ожидается, проверьте, выполняются ли
вообще запросы (смотрите BaseAuthenticate::_query($username)
).
Если запросы не выполняются, проверьте заполняются ли веб-сервером
ключи $_SERVER['PHP_AUTH_USER']
и $_SERVER['PHP_AUTH_PW']
.
Если вы используете Apache с FastCGI-PHP, вам возможно потребуется
добавить следующую строку в ваш корневой файл .htaccess:
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
При каждом запросе данные значения, PHP_AUTH_USER
и PHP_AUTH_PW
, используются
для повторной идентификации пользователя, чтобы убедиться в их подлинности. Как и в
случае с методом объекта аутентификации authenticate()
, метод getUser()
должен возвращать массив с информацией о пользователе, либо false
в случае
неудачи.:
public function getUser(ServerRequest $request)
{
$username = env('PHP_AUTH_USER');
$pass = env('PHP_AUTH_PW');
if (empty($username) || empty($pass)) {
return false;
}
return $this->_findUser($username, $pass);
}
Пример выше показывает, как вы можете реализовать метод getUser()
для Базовой HTTP-аутентификации. Метод _findUser()
является частью
BaseAuthenticate
, и идентифицирует пользователя на основе имени
пользователя и пароля.
Использование базовой аутентификации
Базовая аутентификация позволяет создать аутентификацию без сохранения состояния,
которая может использоваться в приложениях интрасети или для простых сценариев API.
Данные пользователя при базовой аутентификации будут перепроверяться при каждом
запросе.
Предупреждение
Базовая аутентификация передает пользовательские данные в виде открытого
текста. Вы должны использовать протокол HTTPS при использовании Базовой
аутентификации.
Чтобы использовать базовую аутентификацию, вам понадобится настроить
AuthComponent
:
$this->loadComponent('Auth', [
'authenticate' => [
'Basic' => [
'fields' => ['username' => 'username', 'password' => 'api_key'],
'userModel' => 'Users'
],
],
'storage' => 'Memory',
'unauthorizedRedirect' => false
]);
Здесь мы используем имя пользователя + ключ API в качестве наших полей, а
также используем модель Users.
Создание ключей API для Базовой аутентификации
Так как базовый протокол HTTP передает пользовательские данные в виде открытого
текста, было бы неразумно, если бы пользователи передавали свои пароли. Вместо
этого обычно используется непрозрачный ключ API. Вы можете сгенерировать эти
API-токены произвольно, используя библиотеки входящие в состав CakePHP:
namespace App\Model\Table;
use Cake\Auth\DefaultPasswordHasher;
use Cake\Utility\Text;
use Cake\Event\Event;
use Cake\ORM\Table;
class UsersTable extends Table
{
public function beforeSave(Event $event)
{
$entity = $event->getData('entity');
if ($entity->isNew()) {
$hasher = new DefaultPasswordHasher();
// Генерируем 'токен' API
$entity->api_key_plain = Security::hash(Security::randomBytes(32), 'sha256', false);
// Хешируем токен с помощью Bcrypt, чтобы BasicAuthenticate
// мог его проверить при входе.
$entity->api_key = $hasher->hash($entity->api_key_plain);
}
return true;
}
}
Приведенный выше код генерирует случайный хеш для каждого пользователя по мере
их сохранения. В приведенном выше коде предполагается, что у вас есть два поля
api_key
- для хранения хэшированного API-ключа и api_key_plain
- для
открытой текстовой версии ключа API, чтобы мы могли позже отобразить его
пользователю. Использование ключа вместо пароля означает, что даже через
простой протокол HTTP пользователи могут использовать непрозрачный токен вместо
исходного пароля. Также разумно включить логику, позволяющую восстанавливать
ключи API по запросу пользователя.
Использование Дайджест-аутентификации
Дайджест-аутентификация предлагает улучшенную модель безопасности по сравнению с
базовой аутентификацией, так как пользовательские данные никогда не передаются
в заголовке запроса. Вместо этого перается хеш.
Чтобы использовать дайджест-аутентификацию, вам понадобится настроить
AuthComponent
:
$this->loadComponent('Auth', [
'authenticate' => [
'Digest' => [
'fields' => ['username' => 'username', 'password' => 'digest_hash'],
'userModel' => 'Users'
],
],
'storage' => 'Memory',
'unauthorizedRedirect' => false
]);
Здесь мы используем имя пользователя + digest_hash в качестве наших полей и используем
модель Users.
Хеширование паролей для дайджест-аутентификации
Поскольку для Дайджест-аутентификации требуется пароль, хэшированный
в формате, определенном RFC, для правильного хэширования пароля
для использования с Дайджест-аутентификацией вам следует использовать
специальную функцию хэширования пароля из DigestAuthenticate
. Если
вы собираетесь комбинировать дайджест-аутентификацию с любыми другими
стратегиями аутентификации, рекомендуется также сохранить
дайджест-пароль в отдельном поле,отличном от обычного хеша пароля:
namespace App\Model\Table;
use Cake\Auth\DigestAuthenticate;
use Cake\Event\Event;
use Cake\ORM\Table;
class UsersTable extends Table
{
public function beforeSave(Event $event)
{
$entity = $event->getData('entity');
// Создание пароля для дайджест-аутентификации.
$entity->digest_hash = DigestAuthenticate::password(
$entity->username,
$entity->plain_password,
env('SERVER_NAME')
);
return true;
}
}
Пароли для дайджест-аутентификации нуждаются в несколько большем количестве
информации, чем другие хеши паролей для дайджест-аутентификации, основанные
на RFC.
Примечание
Третий параметр метода DigestAuthenticate::password() должен совпадать
со значением параметра конфигурации „realm“, объявленным, когда
DigestAuthentication настраивалось в AuthComponent::$authenticate.
По умолчанию его значение - это env('SCRIPT_NAME')
. Возможно вам
захочется изменить это значение на какую-нибудь статичную строку, если
вы например хотите иметь согласованные хеши в различных окружениях.
Создание кастомных объектов аутентификации
Поскольку объекты аутентификации являются подключаемыми, вы можете
создавать собственные объекты аутентификации в своем приложении или плагинах.
Например, если вы хотите создать объект аутентификации OpenID.
В src/Auth/OpenidAuthenticate.php вы можете указать следующее:
namespace App\Auth;
use Cake\Auth\BaseAuthenticate;
use Cake\Http\ServerRequest;
use Cake\Http\Response;
class OpenidAuthenticate extends BaseAuthenticate
{
public function authenticate(ServerRequest $request, Response $response)
{
// Делаем здесь необходимые действия для OpenID.
// Возвращаем здесь массив с данным о пользователе,
// либо возвращаем false в случае неудачи.
}
}
Объекты аутентификации должны возвращать false
, если идентификация
пользователя не удалась, либо массив с информацией о пользователе в
противном случае. Необязательно наследоваться от класса BaseAuthenticate
,
вы можете просто реализовать интерфейс Cake\Event\EventListenerInterface
.
Класс BaseAuthenticate
предоставляет несколько полезных методов, которые
часто используются. Также вы можете реализовать метод getUser()
, если
ваш объект аутентификации поддерживает аутентификацию без сохранения
состояния или же без использования куки-файлов. Смотрите разделы по
базовой и дайджест-аутентификации ниже для более полной информации.
AuthComponent
запускает два события, Auth.afterIdentify
и Auth.logout
,
после того, как пользователь был идентифицирован и перед его выходом из приложения
соответственно. Вы можете назначить коллбэк-функции для этих событий, задав их
в качестве значений в ассоциативном массиве внутри метода implementedEvents()
вашего класса аутентификации:
public function implementedEvents()
{
return [
'Auth.afterIdentify' => 'afterIdentify',
'Auth.logout' => 'logout'
];
}
Использование кастомных объектов аутентификации
После того как вы создали свои собственные объекты аутентификации, вы
можете использовать их включая их в массив authenticate
компонента
AuthComponent
:
$this->Auth->config('authenticate', [
'Openid', // объект аутентификации приложения.
'AuthBag.Openid', // объект аутентификации плагина.
]);
Примечание
Обратите внимание, что при использовании простых обозначений
при инициализации объекта аутентификации нет слова „Authenticate“.
Если вы все же используете пространства имен, вам нужно будет установить
полное пространство имен класса, включая слово „Authenticate“.
Обработка неаутентифицированных запросов
Когда пользователь, не прошедший проверку подлинности, пытается получить доступ
к защищенным страницам, прежде всего вызывается метод unauthenticated()
последнего вызванного в цепочке аутентификатора. Объект аутентификации может
обрабатывать отправку ответа или перенаправление, возвращая объект ответа,
чтобы указать, что никаких дополнительных действий не требуется. В связи с этим,
порядок, в котором вы указываете провайдера аутентификации в параметре конфигурации
authenticate
, имеет значение.
Если аутентификатор возвращает null
, то AuthComponent
перенаправляет
пользователя на экшен входа(login). Если же это AJAX-запрос, и параметр
конфигурации ajaxLogin
указывает, что элемент визуализируется иначе, то
будет возвращен код состояния HTTP 403.
Вывод флэш-сообщений компонента Auth
Чтобы отображать сообщения об ошибках сессии, генерируемые Auth
, вам нужно
добавить следующий код в свой макет (layout
). Добавьте следующие две
строки в файл src/Template/Layout/default.ctp в разделе body
:
// Все, что необходимо для версий начиная с 3.4.0
echo $this->Flash->render();
// Для версий, предшествующих 3.4.0, потребуется следующее
echo $this->Flash->render('auth');
Вы можете настроить сообщения об ошибках и параметры флэш-сообщений,
используемые AuthComponent
. Используя параметр конфигурации flash
, вы
можете настроить параметры, используемые AuthComponent для установки
флэш-сообщений. Доступные ключи:
key
- Используемый ключ, по умолчанию „default“. В версиях ниже 3.4.0,
по умолчанию использовалось значение „auth“.
element
- Имя элемента использовать для визуализации, по
умолчанию null
.
params
- Массив дополнительных используемых параметров, по
умолчанию []
.
В дополнение к настройкам флэш-сообщений, вы можете настраивать также и
другие сообщения об ошибках, используемые в AuthComponent
. В методе
beforeFilter()
или в настройках компонента вы можете использовать
authError
для кастомизации ошибок использующихся при неудачной
авторизации:
$this->Auth->config('authError', "Упс, вы не авторизованы для получения доступа в этой области.");
Иногда вы хотите отобразить ошибку авторизации только после того, как
пользователь уже выполнил вход в систему. Вы можете подавить это сообщение,
установив его значение в булево false
.
В методе beforeFilter()
вашего контроллера или в настройках компонента:
if (!$this->Auth->user()) {
$this->Auth->config('authError', false);
}
Хеширование паролей
Вы несете ответственность за хэширование паролей, перед тем, как они будут
сохранены в базе данных, самый простой способ - использовать функцию-сеттер
в вашей сущности User
:
namespace App\Model\Entity;
use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;
class User extends Entity
{
// ...
protected function _setPassword($password)
{
if (strlen($password) > 0) {
return (new DefaultPasswordHasher)->hash($password);
}
}
// ...
}
По умолчанию AuthComponent
настроен на использование DefaultPasswordHasher
при проверке учетных данных пользователя, поэтому при аутентификации
пользователей дополнительной настройки не требуется .
DefaultPasswordHasher
использует встроенный алгоритм хэширования bcrypt
,
который является одним из самых сильных решений хэширования паролей, используемых
в отрасли. Хотя рекомендуется использовать этот класс хэширования пароля, дело
может заключаться в том, что вы управляете базой данных пользователей, чей пароль
был захэширован иным способом.
Создание пользовательских классов хеширования паролей
Чтобы использовать другой хешер пароля, вам необходимо создать класс в
src/Auth/LegacyPasswordHasher.php и реализовать методы hash()
и check()
. Этот класс должен наследоваться от класса
AbstractPasswordHasher
:
namespace App\Auth;
use Cake\Auth\AbstractPasswordHasher;
class LegacyPasswordHasher extends AbstractPasswordHasher
{
public function hash($password)
{
return sha1($password);
}
public function check($password, $hashedPassword)
{
return sha1($password) === $hashedPassword;
}
}
После чего вам необходимо настроить AuthComponent для использования вашего
собственного хешера паролей:
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'passwordHasher' => [
'className' => 'Legacy',
]
]
]
]);
}
Поддержка устаревших систем - хорошая идея, но еще лучше сохранить базу
данных с последними достижениями в области безопасности. В следующем
разделе объясняется, как осуществить миграцию с одного алгоритма хеширования
на алгоритм, используемый в CakePHP по умолчанию.
Изменение алгоритмов хэширования
CakePHP предоставляет чистый способ переноса паролей пользователей с одного алгоритма на
другой, это достигается с помощью класса FallbackPasswordHasher
.
Предполагая, что вы переносите приложение с CakePHP 2.x, который использует хэши паролей
sha1
, вы можете настроить AuthComponent
следующим образом:
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'passwordHasher' => [
'className' => 'Fallback',
'hashers' => [
'Default',
'Weak' => ['hashType' => 'sha1']
]
]
]
]
]);
}
Первое имя, появляющееся в ключе hashers
, указывает, какой из классов
является предпочтительным, но будет возвращаться к остальным в списке, если
проверка была неудачной.
При использовании WeakPasswordHasher` вам нужно будет установить значение
``Security.salt
, чтобы гарантировать надежность паролей засчет использования
так называемой «соли».
Чтобы обновить пароли старых пользователей на лету, вы можете изменить функцию
входа соответствующим образом:
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
if ($this->Auth->authenticationProvider()->needsPasswordRehash()) {
$user = $this->Users->get($this->Auth->user('id'));
$user->password = $this->request->getData('password');
$this->Users->save($user);
}
return $this->redirect($this->Auth->redirectUrl());
}
...
}
}
Как вы можете заметить, мы просто устанавливаем простой пароль, так что
функция-сеттер в сущности будет хешировать пароль, как показано в предыдущем
примере, а затем сохранит сущность (entity).
Вход пользователей вручную
-
AuthComponent::setUser(array $user)
Иногда возникает необходимость, когда вам нужно вручную осуществлять вход
пользователя, например, сразу после регистрации в вашем приложении. Вы
можете сделать это вызвав метод $this->Auth->setUser()
с данными того
пользователя, вход которого („login“) вы хотите осуществить:
public function register()
{
$user = $this->Users->newEntity($this->request->getData());
if ($this->Users->save($user)) {
$this->Auth->setUser($user->toArray());
return $this->redirect([
'controller' => 'Users',
'action' => 'home'
]);
}
}
Предупреждение
Убедитесь в том, что вы добавляете вручную id новому пользователю (User
)
в массиве, передаваемом методу setUser()
. В противном случае у вас будет
отсутствовать идентификатор пользователя.
Получение доступа к вошедшим пользователям
-
AuthComponent::user($key = null)
Как только пользователь войдет в систему, вам часто потребуется какая-то
конкретная информация о текущем пользователе. Вы можете получить доступ
к вошедшему в приложение пользователю с помощью метода
AuthComponent::user()
:
// Вызов из контроллера или другого компонента
$this->Auth->user('id');
Если текущий пользователь не вошел в приложение, или же если ключ не
существует, будет возвращено значение null
.
Выход пользователей
-
AuthComponent::logout()
В конце концов вам понадобится быстрый способ разавторизовать кого-то
и перенаправить их туда, куда нужно. Этот метод также полезен, если
вы хотите предоставить ссылку «Выйти из системы» внутри области
вашего приложения для авторизованных пользователей:
public function logout()
{
return $this->redirect($this->Auth->logout());
}
Выход из приложения пользователей, вошедших в него с помощью Дайджест
или Базовой аутентификации, трудно выполнить для всех клиентов.
Большинство браузеров сохраняют учетные данные на протяжении всего
времени их работы. Некоторые клиенты могут быть принудительно выброшены
из приложения, отправкой кода состояния 401. Изменение области
аутентификации - это еще одно возможное решение, которое работает для
некоторых клиентов.
Когда выполнять аутентификацию
В некоторых случаях вы можете захотеть вызвать метод $this->Auth->user()
внутри метода beforeFilter(Event $event)
. Это осуществимо при
использовании ключа конфигурации checkAuthIn
. Для этого внесите
следующие изменения для сопоставления, в каких событиях какие проверки
аутентификации должны быть осуществлены:
//Настройка AuthComponent для аутентификации в методе initialize()
$this->Auth->config('checkAuthIn', 'Controller.initialize');
По умолчанию значение checkAuthIn
- 'Controller.startup'
- но при
использовании 'Controller.initialize'
первоначальная аутентификация
осуществляется перед методом beforeFilter()
.
Авторизация
Авторизация - это процесс подтверждения того, что пользователь, прошедший
идентификацию/аутентифицикацию имееет права доступа к ресурсам, которые
он запрашивает. Если активный `` AuthComponent`` может автоматически
проверить обработчики авторизации и гарантировать, что зарегистрированным
пользователям разрешен доступ к ресурсам, которые они запрашивают.
Существует несколько встроенных обработчиков авторизации, и вы можете
создавать собственные для своего приложения или вкачестве части плагина.
Примечание
Адаптеры ActionsAuthorize
и CrudAuthorize
доступные в CakePHP
2.x теперь перемещены в отдельный плагин cakephp/acl.
Настройка обработчиков авторизации
Вы настраиваете обработчики авторизации, используя ключ конфигурации
authorize
. Вы можете настроить один или несколько обработчиков для
авторизации. Использование нескольких обработчиков позволяет поддерживать
различные способы проверки авторизации. Когда проверяются обработчики
авторизации, они будут вызваны в том порядке, в котором они объявлены.
Обработчики должны возвращать false
, если они не могут проверить
авторизацию, или если проверка не удалась. Обработчики должны возвращать
true
, если проверка авторизации прошла успешно. Обработчики будут
вызываться последовательно до тех пор, пока не найдется подходящий. Если
все проверки закончатся неудачно, пользователь будет перенаправлен на
страницу, с которой он пришел. Кроме того, вы можете остановить все
авторизации, выбросив исключение. Вам нужно будет перехватить любые
выброшенные исключения и обработать их.
Вы можете настроить обработчики авторизации в методе beforeFilter()
или initialize()
вашего контроллера. Вы можете передать информацию о
настройках каждого объекта авторизации, используя массив:
// Простейшая настройка
$this->Auth->config('authorize', ['Controller']);
// Передача параметров
$this->Auth->config('authorize', [
'Actions' => ['actionPath' => 'controllers/'],
'Controller'
]);
По аналогии с authenticate
, authorize
помогает вам соблюдать
принцип DRY (не повторяйся), с помощью ключа all
. Этот специальный
ключ позволяет вам назначать передаваемые параметры каждому
прикрепленному объекту. Ключ all
также можно представить в виде
статического свойства AuthComponent::ALL
:
// Передаем настройки с помощью 'all'
$this->Auth->config('authorize', [
AuthComponent::ALL => ['actionPath' => 'controllers/'],
'Actions',
'Controller'
]);
В примере, приведенном выше, как Actions
так и Controller
получат
все настройки, объявленные для ключа „all“. Все настройки, переданные
конкретному объекту авторизации, переопределят значения соответствующих
ключей, имеющихся внутри ключа „all“.
Если аутентифицированный пользователь пытается перейти к URL-адресу, к
которому у него нет прав доступа, он перенаправляется обратно к
странице-источнику запроса. Если вы не хотите такого перенаправления
(в основном, при использовании адаптера аутентификации без учета состояния),
вы можете установить параметр конфигурации unauthorizedRedirect
в значение false
. Это вынудит AuthComponent
выбросить исключение
ForbiddenException
вместо того, чтобы выполнить перенаправление.
Создание пользовательских объектов авторизации
Поскольку объекты авторизации подключаемы, вы можете создавать
пользовательские объекты авторизации в своем приложении или плагинах.
Например, если вы хотите создать объект авторизации LDAP.
В src/Auth/LdapAuthorize.php вы могли бы поместить следующий код:
namespace App\Auth;
use Cake\Auth\BaseAuthorize;
use Cake\Http\ServerRequest;
class LdapAuthorize extends BaseAuthorize
{
public function authorize($user, ServerRequest $request)
{
// Здесь вы производите все операции, необходимые для LDAP.
}
}
Объекты авторизации должны возвращать false
, если пользователю отказано
в доступе, либо если объект не в состоянии произвести проверку полномочий.
Если же объект способен произвести проверку прав доступа пользователя, то
в таком случае возвращается значение true
. При этом вовсе не обязательно
наследоваться от BaseAuthorize
, достаточно того, чтобы к вашему объекту
авторизации был подключен метод authorize()
. Класс BaseAuthorize
предоставляет множество полезных методов, которые часто используются.
Использование пользовательских объектов авторизации
Как только вы создали ваш объект авторизации, вы можете подключить его,
передав его в массив authorize
вашего компонента AuthComponent
:
$this->Auth->config('authorize', [
'Ldap', // объект авторизации уровня приложения.
'AuthBag.Combo', // объект авторизации плагина.
]);
Отказ от использования авторизации
Если вы все же пожелали не использовать какой-либо из встроенных объектов
авторизации, и захотели обрабатывать события за пределами AuthComponent
,
в таком случае вы можете установить значение
$this->Auth->config('authorize', false);
. По умолчанию AuthComponent
использует для authorize
значение false
. Если вы не используете
схему авторизации, убедитесь, что проверка авторизации производится либо
вручную внутри метода beforeFilter()
вашего контроллера, либо с помощью
какого-нибудь другого компонента.
Создание общедоступных экшенов
-
AuthComponent::allow($actions = null)
Часто у вас может возникать необходимость оставлять общедоступными
некоторые экшены контроллера (не требующие авторизации).
AuthComponent
пессимистичен и по умолчанию закрывает доступ. Вы
можете пометить экшены как общедоступные с помощью метода
AuthComponent::allow()
. AuthComponent
не будет проверять
авторизацию пользователей, обращающихся к таким экшенам, так же,
как не будет проверять объекты авторизации:
// Разрешить все экшены
$this->Auth->allow();
// Разрешить только экшен index.
$this->Auth->allow('index');
// Разрешить только экшены view и index.
$this->Auth->allow(['view', 'index']);
Вызывая метод без параметров, вы откроете доступ ко всем экшенам.
Для уточнения отдельных экшенов, вы передаете их имена в виде строкового значения.
В противном случае передавайте в качестве параметра массив.
Примечание
Вы не должны открывать доступ к экшену «login» вашего контроллера
UsersController
. Подобные действия могут нарушить нормальную работу
компонента AuthComponent
.
Создание экшенов требующих авторизации
-
AuthComponent::deny($actions = null)
По умолчанию все экшены требуют авторизации. И все же, если вы открыли
доступ ко всем экшенам, и хотите обратить этот процесс, вы можете
воспользоваться методом AuthComponent::deny()
:
// Закрыть все экшены.
$this->Auth->deny();
// Закрыть один экшен
$this->Auth->deny('add');
// Закрыть группу экшенов.
$this->Auth->deny(['add', 'edit']);
По аналогии с методом allow()
, если не передается никаких параметров, то
доступ закрыватся ко всем экшенам. Если передается строковое значение, то
доступ закрывается именно к указанному экшену. Для закрытия нескольких экшенов
в метод передается массив
Использование ControllerAuthorize
ControllerAuthorize позволяет обрабатывать проверку авторизации в методах
обратного вызова контроллера. Это идеальный вариант, когда у вас очень
простая авторизация или вам нужно использовать комбинацию моделей и
компонентов для авторизации и вам не хочется создавать пользовательский
объект авторизации.
Коллбек всегда называется isAuthorized()
, и он должен возвращать булево
значение относительно того, разрешен ли пользователю доступ к запрашиваемым
ресурсам. Коллбек передается активным пользователем, таким образом права
этого самого пользователя могут быть проверены:
class AppController extends Controller
{
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authorize' => 'Controller',
]);
}
public function isAuthorized($user = null)
{
// Любой зарегистрированный пользователь может иметь доступ
// к общедоступным функциям
if (!$this->request->getParam('prefix')) {
return true;
}
// Только администоры могут иметь доступ к админке
if ($this->request->getParam('prefix') === 'admin') {
return (bool)($user['role'] === 'admin');
}
// По умолчанию отказ в доступе
return false;
}
}
Вышеприведенный коллбек обеспечит очень простую систему авторизации,
где только пользователи с ролью = admin могут обращаться к экшенам,
у которых задан префикс admin.