Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Вход и права доступа

За вход, роли и закрытые разделы в 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, до второго дело не дойдёт. Располагайте точные адреса выше общих. И скрытая в шаблоне ссылка не защищает страницу: доступ обязательно проверяют на сервере.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.