Как проверять юзера при каждой аутентификации в Symfony 5?

Есть админ и обычные юзеры. Обычного юзера могут заблокировать в любой момент - тогда надо при первой же аутентификации проверить не заблокирован ли он - если да - разлогинить и отправить на форму авторизации.
Пытался через EventListener - но ничего не происходит - AuthListener не вызывается.
На event: security.interactive_login тоже нет реакции

#config/services.xml
services:
    App\EventListener\AuthListener:
        tags:
            - { name: kernel.event_listener, event: security.authentication.success, method: onSecurityAuthenticationSuccess }


//App/EventListener/AuthListener.php
namespace App\EventListener;

use App\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Security;

class AuthListener
{
    /** @var User $user */
    private $user;

    public function __construct(Security $security)
    {
        $this->user = $security->getUser();
    }

    public function onSecurityAuthenticationSuccess()
    {
        if ($this->user->isBlocked()) {
            file_put_contents('onSecurityAuthenticationSuccess.txt', $this->user->getId() . ' - User blocked', FILE);
            return new RedirectResponse('/logout');
        }
    }
}


Заранее спасибо
  • Вопрос задан
  • 463 просмотра
Решения вопроса 1
@Kripto77 Автор вопроса
Спасибо всем за комментарии - навели на нужные идеи.
По итогу пришел к не очень красивому решению через EventSubscriber и глобальный onKernelRequest (срабатывает на любой запрос). Буду признателен если кто подскажет решение получше.

//src/Events/GlobalSubscriber.php

namespace App\Events;

use App\Entity\User;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class GlobalSubscriber implements EventSubscriberInterface
{
    /** @var RouterInterface $router */
    private $router;

    /** @var TokenStorageInterface $tokenStorage */
    private $tokenStorage;

    /**
     * @param RouterInterface $router
     * @param TokenStorageInterface $tokenStorage
     */
    public function __construct(RouterInterface $router, TokenStorageInterface $tokenStorage)
    {
        $this->router = $router;
        $this->tokenStorage = $tokenStorage;
    }

    public function onKernelRequest(RequestEvent $event)
    {
        if(!$event->isMasterRequest())
            return;

        $this->isUserBlocked($event);
    }

    /**
     * Проверка блокировки пользователя
     * @param RequestEvent $event
     */
    private function isUserBlocked(RequestEvent $event)
    {
        if (empty($this->tokenStorage->getToken()) || !$this->tokenStorage->getToken()->getUser() instanceof User) return;

        /** @var User $user */
        $user = $this->tokenStorage->getToken()->getUser();

        if ($user->isBlocked()) {
            $event->setResponse(new RedirectResponse($this->router->generate('app_logout')));
        }

    }

    public static function getSubscribedEvents()
    {
        return [
            // Правка от @Flying
            KernelEvents::REQUEST => 'onKernelRequest'
            // Старый вариант - RequestEvent::class => 'onKernelRequest'
        ];
    }
}


Странно почему не заработал EventListener - делал по офф.докам https://symfony.com/doc/current/event_dispatcher.h...
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@dzhebrak
Возможно, наиболее простым решением будет при блокировке пользователя удалять у него роли (User->setRoles([])). Тогда у заблокированного пользователя на следующем запросе будут разные данные в сессии и в бд, и его перенаправит на форму входа в аккаунт. Там через UserChecker можно показать ему сообщение, что аккаунт заблокирован.

https://symfony.com/doc/current/security/user_prov...

Разумеется для этого нужно, чтобы в сесии хранились роли пользователя (по умолчанию symfony так и делает), поэтому если ваш класс User имплементирует \Serializable, то нужно в serialize/unserialize или добавить роли, или сам флаг блокировки.

Еще вариант - проверять статус в UserProvider в методе refreshUser() и бросать исключение UsernameNotFoundException.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы