Нюанс с параметром конфигурации storage компонента Auth в CakePHP 3

При настройке компонента Auth в CakePHP 3, можно одновременно подключить несколько объектов аутентификации, используя параметр конфигурации authenticate. Такая необходимость может возникнуть для предоставления пользователям альтернативных способов входа в систему. Но вот незадача, значение параметра конфигурации storage должно быть одинаковым для всех подключаемых объектов аутентификации.

Настоятельно рекомендую ознакомиться с документацией к AuthComponent. В ней достаточно подробно описаны возможности компонента, его настройка, создание пользовательских объектов аутентификации и авторизации, классов хеширования паролей и многое другое. В этой же статья будут затронуты только нюансы с параметрами конфигурации authenticate и storage, в вопросах использования нескольких объектов аутентификации, с различными способами сохранения состояния.

Базовые настройки AuthComponent

В CakePHP 3 встроено несколько объектов аутентификации, используемые в AuthComponent:

  • FormAuthenticate (аутентификация с помощью HTML форм);
  • BasicAuthenticate;
  • DigestAuthenticate.

FormAuthenticate является самым популярным способом аутентификации для пользователей веб-браузеров и основан на передаче учётных данных методом POST, а режим сохранения состояния поддерживается с помощью механизма Session.

Для подключения AuthComponent достаточно, в методе initialize() контроллера, прописать следующий код:

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Auth');
    }
}

По умолчанию, объектом аутентификации выступает FormAuthenticate, а класс сохранения состояния (параметр конфигурации storage) имеет значение Session.

Для явного указания объекта аутентификации и класса сохранения состояния, можно воспользоваться следующим кодом:

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Auth', [
            'authenticate' => 'Form',
            'storage'      => 'Session'
        ]);
    }
}

Чтобы подключить несколько объектов аутентификации, достаточно параметру конфигурации authenticate передать массив значений:

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Auth', [
            'authenticate' => ['Form', 'Basic'],
            'storage'      => 'Session'
        ]);
    }
}

В этом случае, объекты аутентификации будут проверяться в обратном порядке их объявления. То есть, первым будет проверен объект BasicAuthenticate и, если, проверка завершится неудачно, будет проверен объект FormAuthenticate.

Нюанс с параметров конфигурации storage

Пример выше показателен. Объекты аутентификации Form и Basic используют разные классы сохранения состояния. Для объекта Form, значение параметра storage должно быть Session, а объектов Basic и Digest должно быть Memory.

Многие спросят, а что здесь такого? Пусть, для объектов аутентификации Basic и Digest, создаётся не нужная сессия. Некоторые поддерживали такой подход. Работает и ладно. Но я считаю, что это не выход. Необходимо использовать технологии только там, где это необходимо и исключать все издержки и «костыли».

Было бы здорово, если значение параметра конфигурации storage задавалось для каждого объекта аутентификации индивидуально, либо система сама определяла какой класс сохранения состояния необходим для каждого из этих объектов. Параметр конфигурации storage обсуждался с разработчиками CakePHP и были указаны причины такой реализации:

  1. Параметр конфигурации storage используется не только объектами аутентификации, но и объектами авторизации, поэтому, указание данного параметра для каждого объекта аутентификации приведёт к неименуемой путанице;
  2. Кроме трёх перечисленных объектов аутентификации, в сообществе CakePHP множество пользовательских объектов аутентификации, которые сложно будет поддерживать при изменение конфигурации.

В результате, было принято решение использовать, по умолчанию, механизм Session как базовый класс сохранения состояния.

Использование объектов аутентификации с разными классами сохранения состояния

Ключ к совместному использованию объектов аутентификации, с разными классами сохранения, — в раздельном подключение.

Предположим, дополнительно к стандартным функциям сайта, получить доступ к которым можно с помощью объекта аутентификации Form, разработаем простое API, доступное через объекты Basic и Digest.

Адреса функций API:

  • /posts.json — список статей;
  • /posts/$id.json — описание статьи с идентификатором $id.

В результате успешного запроса сервер выдаёт данные в JSON формате. Соответственно, если расширение файла равно json, используем объекты аутентификации без сохранения состояния. Данное условие добавим в метод beforeFilter() контроллера, изменяя параметры конфигурации, с помощью метода $this->Auth->config().

namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\Event;

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Auth');
    }

    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);

        if ($this->request->params['_ext'] == 'json') {
            $this->Auth->config('authenticate', ['Basic', 'Digest']);
            $this->Auth->config('storage', 'Memory');
        }
    }
}

Кроме изменения объектов аутентификации, изменению подвергается и параметр конфигурации storage.

Заключение

Нюанс с параметром конфигурации storage показывает на сколько не гибко текущие решение, что не умоляет заслуг разработчиков компонента Auth и всего фреймворка.

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