[Перевод] Symfony2: logging out
Одно из золотых правил Symfony2 — никогда не хардкодить внутри кода или шаблонов какие-либо ссылки и пути. Соблюдение этого правила и генерация ссылок через роутер значительно облегчат вашу жизнь. Однако есть одна вещь, которую я часто наблюдаю: люди продолжают хардкодить ссылки на выход из системы, например, как »/logout», только вот сам процесс логаута немного сложнее, чем может казаться и использование такой ссылки может работать в большинстве случаев, но это не будет лучшим решением проблемы.Немного информации о компоненте (и бандле) Symfony2 SecurityБольшинство разработчиков знают, что можно сделать несколько защищенных разделов внутри одного проекта. Например, это может быть панель для обычных пользователей (зарегистрированные пользователи) с путем /secure. И, возможно, у вас в проекте может быть отдельная панель администрирования по адресу /admin и есть отдельная зона для пользователей API, которая находится в разделе с адресом /api. Также, можно сделать «защищенную зону», которой и вовсе не нужна защита — такой подход используется в тулбаре Symfony2 для разработчиков. В конце концов, можно вообще все это перенести в одну большую зону, в которой будет реализовано несколько вариантов определения кто же имеет доступ к защищенной части проекта. Вообще, хоть разделение проекта на отдельные зоны и делает ваш проект сложнее, это дает некоторые преимущества.Каждая из защищенных зон вызывает свой файрвол, который и определяет, аутентифицировать пользователя или нет. Каждый файрвол отделен от других: если вы аутентифицировались в одном из них, это не значит, что вы автоматически аутентифицированы в других и есть лишь один активный файрвол (тот самый, который совпал с шаблоном URL). Это имеет значение, так как разные файрволы могут использовать разные базы данных или просто использовать разные способы аутентификации (например, в API можно использовать OAuth Token, в то время как остальные разделы могут использовать форму для входа).
Это также означает, что каждый файрволл имеет разные пути логаута, а для некоторых из них логаут как таковой и не существует. Пример security.yml
# Раздел разработчика dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false # Раздел админа superadminstuff: pattern: ^/admin http_basic: provider: memory_user_provider realm: «Super Admin section!» # все остальное с обычной формой для входа main: pattern: ^/ form_login: provider: fos_userbundle csrf_provider: form.csrf_provider login_path: /login logout: true Это блок «firewall» в security.yml и в нем определено 3 файрвола — dev, superadminstuff и main. dev вообще не использует аутентификацию (security=false), что означает, что доступ разрешен всем и пути »/js»,»/css» и другие не управляются файрволом main.Следующий набор правил защищает зону администрирования. В ней используется http_basic в качестве входа, то есть браузер покажет диалоговое окно, в котором попросит вас указать логин и пароль (на самом деле это не очень безопасно, так как они будут передаваться как plain text). Более того — браузер будет отправлять логин и пароль при каждом запросе к проекту. Symfony2 может проверить эти данные используя провайдер «memory_user_provider», блок которого я не привел, но в нем, обычно, указывается несколько стандартных пользователей и их логин/пароль (прямо в файле конфигурации, а не в базе данных).
В http-basic в действительности нет логаута потому, что единственный способ выхода в таком случае — прекратить отправлять запросы. Очистка кеша или или перезапуск браузера обычно помогает сделать логаут в таком случае.
Последний файрвол — main. Вместо http-basic он использует форму для входа. Здесь используется FOSUserBundle, в котором есть своя форма входа и методы для ее обработки, поэтому единственное что требуется от разработчика — немного кастомизировать их, а не писать свои.
В случае, если вы открываете страницу в этом файрволе, и не вошли ранее — Symfony2 автоматически перенаправит пользователя на страницу входа, которая указана в в параметре login_path в блоке form_login. Обычно (по-умолчанию), это путь с адресом /login. Как только пользователь вошел, Symfony2 сохранит пользователя и роль внутри его сессии и при следующем запросе пользователю не придется входить снова.
Выход из такого файрвола довольно прост — нужно перейти на его страницу выхода. Но что это за страница?
В примере выше, используется параметр «logout: true». Стоит обратить внимание, что этот параметр находится в блоке файрвола, а не в блоке form_login. Указывая logout: true, мы говорим Symfony2 использовать стандартные настройки логаута, а именно:
logout: csrf_parameter: _csrf_token csrf_token_generator: ~ csrf_token_id: logout path: /logout target: / success_handler: ~ invalidate_session: true delete_cookies: name: path: null domain: null handlers: [] Как можно заметить, указывается путь, по которому будет происходить выход. Но есть одна странность: по-умолчанию, логаут листенер запускается перед вызовом какого-нибудь контроллера или экшена, а затем делает редирект на страницу, указанную в параметре «target». Если у вас свой обработчик logout-события, который указывается в параметре «handlers», и он НЕ возвращает объект HTTP Response, то вызывается текущий роут. То есть по-умолчанию, ваши контроллер/экшн не будут вызваны, НО они должны быть указаны (то есть, роутер Symfony2 обязать знать о нем). По этой причине можно найти странный экшн logout в FOSUserBundle, бросающий исключение, так как он никогда не будет вызван.Логаут Итак, что же делать с выходом? В первую очередь, не стоит хардкодить URL. Даже если вы используете маршрут вместо url, вы можете поменять его внутри конфигурации и выход перестанет работать. Что действительно стоит делать — указывать через twig ссылку или маршрут, указанный в конфигурации. К счастью, SecurityBundle имеет расширение для twig, который поможет это сделать. Речь идет о функциях logout_url и logout_path. Эти функции получают на вход id файрвола (например, «main», «dev» и т.д.) и генерирует правильный адрес выхода для него: Logout В этом случае произойдет выборка правильного адреса и в качестве бонуса добавится csrf-token, если это было указано в конфигурации. Таким образом, вместо того, чтобы указать в шаблоне адрес страницы, нужно указывать тот файрвол, который используется в данный момент.Правда, теперь ваши шаблоны знают больше чем надо и необходимо указать имя файрвола вручную. В большинстве случаев это нормально, но иногда это может вызвать проблемы (например, если вы используете меню, где используется twig). Чтобы избежать проблем с этим существует возможность получить имя текущего файрвола, пусть немного и неправильная:
Logout Внутри токена контекста безопасности находится необходимое нам название текущего файрвола. «Неправильность» решения в том, что глобальная переменная app.security в Symfony версии 2.6 будет в статусе deprecated и удалена в версии 3.0. Со временем, уверен, будут и другие пути генерации пути для выхода.