Вход и права доступа
За вход, роли и закрытые разделы в Symfony отвечает компонент Security. Большую часть кода создают команды, а правила доступа описывают в одном файле — config/packages/security.yaml.
Пользователь
php bin/console make:user
php bin/console make:migration
php bin/console doctrine:migrations:migrateКоманда создаст сущность User с полями email, password и roles и пропишет её в настройках безопасности.
Форма входа и регистрация
php bin/console make:security:form-login
php bin/console make:registration-formПервая команда создаёт контроллер, шаблон входа и маршруты /login и /logout, вторая — страницу регистрации.
Хеширование пароля
Пароль никогда не хранят открытым текстом. Его хеширует сервис:
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
public function register(UserPasswordHasherInterface $hasher, EntityManagerInterface $em): Response
{
$user = new User();
$user->setEmail('anna@example.com');
$user->setPassword($hasher->hashPassword($user, 'secret-password'));
$em->persist($user);
$em->flush();
// …
}Закрытые разделы
Правила по адресам записывают в security.yaml:
security:
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }Гостя, открывшего /profile, Symfony отправит на страницу входа. Пользователь без роли ROLE_ADMIN на /admin получит ошибку 403.
Роли
Роль — строка, начинающаяся с ROLE_. Роль ROLE_USER есть у каждого вошедшего. Старшие роли могут включать младшие:
security:
role_hierarchy:
ROLE_ADMIN: ROLE_EDITOR
ROLE_EDITOR: ROLE_USERПроверка в контроллере
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route('/admin/posts', name: 'admin_posts')]
#[IsGranted('ROLE_ADMIN')]
public function posts(): Response
{
$user = $this->getUser();
// …
}Атрибут можно поставить и над классом — тогда он действует на все методы. Внутри метода проверку делает $this->denyAccessUnlessGranted('ROLE_EDITOR').
Проверка в шаблоне
{% if is_granted('ROLE_ADMIN') %}
<a href="{{ path('admin_posts') }}">Управление</a>
{% endif %}
{% if app.user %}
<p>Вы вошли как {{ app.user.email }}</p>
<a href="{{ path('app_logout') }}">Выйти</a>
{% else %}
<a href="{{ path('app_login') }}">Войти</a>
{% endif %}Права на конкретную запись
Правило «редактировать статью может только её автор» выносят в отдельный класс — избиратель (voter). Его создаёт make:voter, а проверяют так же:
$this->denyAccessUnlessGranted('POST_EDIT', $post);Частая ошибка
Правила access_control проверяются сверху вниз, и срабатывает первое подходящее. Если поставить общее правило ^/ выше, чем ^/admin, до второго дело не дойдёт. Располагайте точные адреса выше общих. И скрытая в шаблоне ссылка не защищает страницу: доступ обязательно проверяют на сервере.