Почему Symfony Guard запускает событие security.interactive_login при каждом запросе?

У меня есть приложение Symfony 3.2, которое предоставляет REST API и использует Json Web Tokens (JWT) для аутентификации. Недавно я перешел на использование компонента Symfony’s Guard. Теперь мой security.yml содержит следующий раздел конфигурации брандмауэра (я использую пакет Lexik JWT 2.4.0, но это не имеет значения):

firewalls:
    # ...
    api:
        pattern:   ^/api
        stateless: true
        guard:
            authenticators:
               - lexik_jwt_authentication.jwt_token_authenticator

Поскольку я сделал это переключение, я заметил, что каждый запрос обрабатывается так, как если бы пользователь только что вошел в систему, т.е. запускается событие security.interactive_login. В документации (https://symfony.com/doc/current/components/security/authentication.html#authentication-events) говорится:

Событие security.interactive_login запускается после того, как пользователь активно вошел на ваш сайт. Важно отличать это действие от неинтерактивных методов аутентификации, таких как: аутентификация на основе файла cookie «запомнить меня», аутентификация на основе вашего сеанса, аутентификация с использованием базового HTTP-заголовка или дайджеста-заголовка HTTP. Вы можете прослушивать событие security.interactive_login, например, чтобы выдавать вашему пользователю приветственное флэш-сообщение каждый раз, когда он входит в систему.

Поэтому я определенно не ожидаю этого события для каждого запроса — я бы предпочел получать событие security.authentication.success при каждом запросе, как указано в документации.

Однако класс GuardAuthenticatorHandler Symfony отправляет событие security.interactive_login в своем authenticateWithToken методе, и этот метод вызывается GuardAuthenticationListener при каждом запросе. Это ошибка в Symfony, недоразумение с моей стороны или из-за неправильной конфигурации?

(Это не философский вопрос — в моем случае он приводит к конкретной проблеме, заключающейся в том, что время последнего входа пользователя в систему обновляется при каждом запросе, что не имеет смысла.)

вы нашли решение? У меня точно такая же проблема, и я хочу чистое решение, а не упоминание о взломе ниже …   —  person dwytrykus    schedule 20.03.2020

См. также:  Есть ли способ очистить кешированные переменные в Firebase Cloud Functions?

К сожалению, я не нашел подходящего решения.   —  person dwytrykus    schedule 23.03.2020

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 4
  1. dwytrykus

    Вы должны изменить это

            stateless: false
    

    Можете объяснить почему? Мне кажется, что запросы с аутентификацией JWT используются для разговора без сохранения состояния. person dwytrykus; 24.05.2017

    Это объясняется здесь symfony.com/doc/ current / security /, если вы устанавливаете без сохранения состояния true, вы не сохраняете аутентификацию в сеансе, и ожидается, что вы будете аутентифицироваться каждый раз. person dwytrykus; 25.05.2017

  2. dwytrykus

    Я столкнулся с вашей проблемой, потому что у меня точно такая же проблема. Мой обходной путь — добавить атрибут в объект запроса прямо перед возвратом true в методе поддержки охранника.

    Пример:

    public function supports(Request $request)
    {
        ...
    
        $request->attributes->set('is_interactive_login', true);
    
        return true;
    }
    

    С помощью этой информации вы можете проверить, был ли это интерактивный вход в прослушиватель событий.

    Пример:

    public function onLoginSuccess(InteractiveLoginEvent $event)
    {
        $request = $event->getRequest();
        if ($request->attributes->get('is_interactive_login', false)) {
            // do whatever you need todo on interactive login
        }
    }
    
  3. dwytrykus

    Лучше подписаться на событие Events::JWT_CREATED, поскольку оно запускается после прохождения аутентификации с учетными данными.

    Пример:

    <?php
    
    namespace App\Event\Subscriber;
    
    use App\Entity\User\User;
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\EntityManagerInterface;
    use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
    use Lexik\Bundle\JWTAuthenticationBundle\Events;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    class AuthenticationSuccessSubscriber implements EventSubscriberInterface
    {
        /**
         * @var EntityManager
         */
        private $em;
    
        public function __construct(EntityManagerInterface $em)
        {
            $this->em = $em;
        }
    
        public static function getSubscribedEvents()
        {
            return [
                Events::JWT_CREATED => 'onInteractiveLogin',
            ];
        }
    
        /**
         * @param JWTCreatedEvent $event
         *
         * @throws \Doctrine\ORM\ORMException
         * @throws \Doctrine\ORM\OptimisticLockException
         */
        public function onInteractiveLogin(JWTCreatedEvent $event)
        {
            /** @var User $user */
            $user = $event->getUser();
            $user->setLastLoginAt(new \DateTime());
            $user->resetFailedLogins();
            $this->em->flush($user);
        }
    }
    
  4. dwytrykus

    Я столкнулся с той же проблемой. В моем случае я использую аутентификацию Guard для запросов API. Так что мне определенно не нравится обновлять last_login пользователя после любого запроса API.

    Событие INTERACTIVE_LOGIN отправляется из здесь..

    Итак, мой грязный прием — добавить это определение в раздел services конфигурации приложения:

    security.authentication.guard_handler:
            class: Symfony\Component\Security\Guard\GuardAuthenticatorHandler
            arguments:
                $eventDispatcher: ~
    

    Предупреждение

    Очевидным недостатком этого подхода является то, что вы сломали обработчик изменений для всех средств защиты вашего приложения.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: