首页 > 解决方案 > Symfony 4.4.2 - 带有 TokenStorageInterface 的 EventListener 使调试栏显示“加载 Web 调试工具栏时发生错误”。

问题描述

我刚刚将一个应用程序从 Symfony 4.3.9 升级到 4.4.2。之后,我的调试栏无法正常工作并显示“加载 Web 调试工具栏时发生错误。” 经过长时间的调查,我发现这是由于 security.authentication.failure 事件上的 EventListener 造成的。评论 onAuthenticationFailure 方法内容什么也没做,经过一番调查,它在从标签和构造函数中删除 TokenStorageInterface 时起作用......但我需要它。

有任何想法吗?

这是代码:

服务.yaml

    App\EventListener\LoginListener:
    arguments: ["@doctrine", "@security.token_storage", "@router", "@event_dispatcher"]
    tags:
        - { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure }

登录监听器.php

<?php

namespace App\EventListener;

use App\Entity\AdminUser;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;

/**
 * Class LoginListener
 * Listens to user log in events (failure, interactive log in) to provide additionnal security measures
 *
 * @package App\EventListener
 */
final class LoginListener
{
    protected $doctrine;
    protected $request;
    protected $tokenStorage;
    protected $router;
    protected $dispatcher;

    /**
     * Login constructor.
     *
     * @param Registry                 $doctrine
     * @param TokenStorageInterface    $tokenStorage
     * @param RouterInterface          $router
     * @param EventDispatcherInterface $dispatcher
     */
    public function __construct(
        Registry $doctrine,
        TokenStorageInterface $tokenStorage,
        RouterInterface $router,
        EventDispatcherInterface $dispatcher
    ) {
        $this->doctrine = $doctrine;
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
        $this->dispatcher = $dispatcher;
    }

    /**
     * @param AuthenticationFailureEvent $event
     * @throws ORMException
     * @throws OptimisticLockException
     */
    public function onAuthenticationFailure(AuthenticationFailureEvent $event)
    {
        /** @var EntityManager $em */
        $em = $this->doctrine->getManager();
        $username = $event->getAuthenticationToken()->getUsername();
        /** @var AdminUser $user */
        $user = $em->getRepository(AdminUser::class)->findOneBy(['username' => $username]);
        if ($user instanceof AdminUser) {
            $user->addFailedLogin();
            if ($user->getFailedLogin() == 5) {
                $user->setLocked(1);
            }
            $em->persist($user);
            $em->flush();
        }
    }
}

谢谢 :)

---EDIT--- 事实上,听众是另一个人的编辑。它不需要 TokenStorage 但我会在不久的将来遇到问题:

<?php

namespace App\XXXBundle\EventListener;

use App\XXXBundle\Entity\AdminUser;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

/**
 * Class Login
 * Listens to user log in events (failure, interactive log in) to provide additionnal security measures
 *
 * @package App\XXXBundle\EventListener
 */
class Login
{
    protected $doctrine;
    protected $request;
    protected $tokenStorage;
    protected $router;
    protected $dispatcher;

    /**
     * Login constructor.
     *
     * @param Registry                 $doctrine
     * @param TokenStorage             $tokenStorage
     * @param RouterInterface          $router
     * @param EventDispatcherInterface $dispatcher
     */
    public function __construct(
        Registry $doctrine,
        TokenStorage $tokenStorage,
        RouterInterface $router,
        EventDispatcherInterface $dispatcher
    ) {
        $this->doctrine = $doctrine;
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
        $this->dispatcher = $dispatcher;
    }

    /**
     * @param AuthenticationFailureEvent $event
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function onAuthenticationFailure(AuthenticationFailureEvent $event)
    {
        /** @var EntityManager $em */
        $em = $this->doctrine->getManager();
        $userName = $event->getAuthenticationToken()->getUsername();
        /** @var AdminUser $user */
        $user = $em->getRepository(AdminUser::class)->findOneByUsername($userName);
        if ($user instanceof AdvancedUserInterface) {
            $user->addFailedLogin();
            if ($user->getFailedLogin() == 5) {
                $user->setLocked(1);
            }
            $em->persist($user);
            $em->flush();
        }
    }

    /**
     * @param InteractiveLoginEvent $event
     * @throws \Exception
     */
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();
        if ($user instanceof AdvancedUserInterface) {
            $em = $this->doctrine->getManager();
            if ($user->getLocked()) {
                $this->tokenStorage->setToken(null);
                throw new CustomUserMessageAuthenticationException('Compte verrouillé.');
            }
            if ($user->getExpiresAt() && $user->getExpiresAt() <= new \DateTime()) {
                $user->setIsActive(0);
                $em->persist($user);
                $em->flush();
                $this->tokenStorage->setToken(null);
                throw new CustomUserMessageAuthenticationException('Compte expiré.');
            }
            if ($user->getCredentialsExpireAt() && $user->getCredentialsExpireAt() <= new \DateTime()) {
                $this->dispatcher->addListener(KernelEvents::RESPONSE, [$this, 'redirectToCredentialsChange']);
            }
            $user->setLastLogin(new \DateTime());
            $user->setFailedLogin(0);
            $em->persist($user);
            $em->flush();
        }

    }

    public function redirectToCredentialsChange(ResponseEvent $event)
    {
        $event->getResponse()->headers->set('Location', $this->router->generate('admin_security_changecredentials'));
    }
}

标签: symfonysymfony4

解决方案


推荐阅读