Аутентификация в Symfony 7: пошаговая инструкция
Настроим вход по email и паролю в Symfony 7.x с нуля: сущность User, форма логина, security.yaml и защита маршрутов. На всё уйдёт примерно час.

В конце вы получите работающий вход на сайт: пользователь заходит на /login, вводит email и пароль, попадает на защищённую страницу, а без входа его туда не пускают. Разберём это на Symfony 7.x с новой системой аутентификаторов (Security компонент версии 7.x, устаревшая система Guard из статьи исключена — она удалена ещё в Symfony 6). Времени на всё — около часа, если проект уже развёрнут.
Что нужно иметь до начала
- PHP 8.2 или новее — минимальное требование Symfony 7.x;
- Composer и установленный проект Symfony (
symfony new my_app --webappили существующий); - настроенное подключение к базе данных в
.env(переменнаяDATABASE_URL) и запущенная СУБД — примеры даны под Doctrine ORM; - установленный
symfony/maker-bundleв dev-зависимостях — он сгенерирует бо́льшую часть кода.
Проверьте версию: php bin/console --version должна показать Symfony 7.x. Если её нет, дальше синтаксис атрибутов и конфигов может отличаться.
Шаг 1. Ставим нужные пакеты
Security-компонент и maker-bundle ставятся отдельными пакетами:
composer require symfony/security-bundle
composer require --dev symfony/maker-bundle
После установки в config/packages/ появится файл security.yaml. Это и есть центр настройки. Проверка: команда php bin/console debug:config security выводит конфигурацию без ошибок.
Шаг 2. Создаём сущность User
Symfony ждёт класс, реализующий UserInterface и PasswordAuthenticatedUserInterface. Maker сделает его сам и правильно:
php bin/console make:user
Команда задаст четыре вопроса:
- имя класса — оставьте
User; - хранить ли в базе через Doctrine — ответьте
yes; - поле-идентификатор для входа — введите
email; - нужно ли хешировать пароли — ответьте
yes.
В результате появится src/Entity/User.php с полями email, roles, password и методами getUserIdentifier(), getRoles(), getPassword(). В security.yaml автоматически пропишется секция password_hashers и провайдер пользователей на основе сущности.
Проверка: откройте src/Entity/User.php — метод getUserIdentifier() должен возвращать $this->email.
Применяем миграцию
php bin/console make:migration
php bin/console doctrine:migrations:migrate
После этого в базе появится таблица user с уникальным индексом по email. Проверка: php bin/console dbal:run-sql "SELECT * FROM user" отрабатывает и возвращает пустой результат.
Шаг 3. Генерируем форму логина
Отдельный аутентификатор для формы входа тоже делает maker:
php bin/console make:auth
Выберите вариант Login form authenticator. Команда спросит имя класса аутентификатора (можно оставить AppAuthenticator), имя контроллера логина (SecurityController) и предложит создать logout-маршрут — согласитесь.
Появятся три вещи:
src/Security/AppAuthenticator.php— логика проверки логина;src/Controller/SecurityController.phpс методамиlogin()иlogout();templates/security/login.html.twig— шаблон формы.
В security.yaml maker сам пропишет секцию firewalls с ключами custom_authenticator и logout. Проверка: в security.yaml внутри firewalls.main появилась строка custom_authenticator: App\Security\AppAuthenticator.
Не редактируйте security.yaml вручную до того, как maker закончит. Порядок ключей и отступы в YAML критичны, а генератор ставит их корректно — правьте уже готовый файл.
Шаг 4. Указываем, куда пускать после входа
В сгенерированном AppAuthenticator.php метод onAuthenticationSuccess() по умолчанию кидает исключение с напоминанием указать маршрут. Замените тело на редирект — например, на главную:
// src/Security/AppAuthenticator.php
public function onAuthenticationSuccess(
Request $request,
TokenInterface $token,
string $firewallName
): ?Response {
// если пользователь шёл на защищённую страницу — вернём его туда
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// иначе — на главную
return new RedirectResponse($this->urlGenerator->generate('app_home'));
}
Здесь app_home — имя вашего главного маршрута. Если такого маршрута нет, создайте контроллер командой php bin/console make:controller HomeController и используйте его имя. Проверка: перезагрузите страницу — исключение You must configure the check path... больше не появляется.
Шаг 5. Создаём первого пользователя
Регистрации у нас пока нет, поэтому пользователя проще завести временной командой. Быстрый способ — консоль Doctrine через простой fixture или ручной insert с уже захешированным паролем. Хеш можно получить так:
php bin/console security:hash-password
Команда попросит ввести пароль и выдаст готовый хеш. Вставьте его в базу вместе с email:
php bin/console dbal:run-sql "INSERT INTO user (email, roles, password) VALUES ('user@example.com', '[]', 'СЮДА_ХЕШ')"
Для боевого проекта так делать не нужно — там будет форма регистрации (make:registration-form). Здесь задача — просто получить учётку для проверки входа.
Шаг 6. Защищаем маршрут и проверяем вход
Пометьте контроллер, который должен требовать входа, атрибутом:
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route('/dashboard', name: 'app_dashboard')]
#[IsGranted('ROLE_USER')]
public function dashboard(): Response
{
return $this->render('dashboard/index.html.twig');
}
Теперь запустите сервер (symfony serve) и откройте /dashboard. Проверка последовательности:
- без входа вас перебросит на
/login; - введёте email и пароль созданного пользователя — попадёте на
/dashboard; - откроете
/logout— сессия сбросится,/dashboardснова недоступен.
Если не получилось
Invalid credentials.— email или пароль не совпадают. Частая причина: пароль в базе не захеширован (вставили открытый текст). Пересоздайте хеш черезsecurity:hash-password.Unable to find the controller for path "/login_check"— в шаблоне формыactionуказывает не на тот маршрут, что вcheck_path. Сверьтеcheck_pathвsecurity.yamlс атрибутом формы вlogin.html.twig.The security token could not be foundили бесконечный редирект на /login — маршрут/loginсам попал подIsGrantedили подaccess_control. Логин-страница должна быть доступна всем: не защищайте её.Class "App\Entity\User" ... must implement UserInterface— правили сущность руками и убралиimplements UserInterface. Верните интерфейсы, которые проставилmake:user.- После входа снова кидает на форму — не настроен провайдер или в
security.yamlвprovidersуказано не то поле (property). Оно должно совпадать с идентификатором изmake:user—email.
Что делать дальше
Базовый вход работает. Следующие шаги по мере роста проекта:
- форма регистрации —
php bin/console make:registration-form, она сама подключит хеширование и опционально подтверждение email; - роли и иерархия доступа — секция
role_hierarchyвsecurity.yamlи проверки через#[IsGranted]или голосователи (voters); - «запомнить меня» — параметр
remember_meв фаерволе; - вход через сторонние сервисы (Google, GitHub) — пакет
knpuniversity/oauth2-client-bundle.
Инвалидация сессий и защита от перебора паролей (login throttling) — отдельные темы; здесь мы собрали минимальный рабочий вход. За точным синтаксисом конфигов держите под рукой официальную документацию Security для вашей минорной версии.
Карточки для маркетплейсов: Таблица товаров и папка фотографий на входе — комплект слайдов и описаний на выходе, по требованиям Wildberries и Ozon.
Спросить за 5 ₽Источники: Symfony — Security (официальная документация), Symfony — How to Build a Login Form
Частые вопросы
У меня Symfony 6, а не 7 — инструкция подойдёт?
Обязательно ли использовать maker-bundle?
После логина бесконечный редирект на /login — что не так?
Можно логинить по username вместо email?
Как хешируются пароли и не нужно ли выбирать алгоритм вручную?
Достаточно ли этого для продакшена?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.