简体   繁体   English

为什么Symfony Guard会在每次请求时触发'security.interactive_login'事件?

[英]Why does Symfony Guard fire 'security.interactive_login' event on every request?

I have a Symfony 3.2 application which exposes a REST API and uses Json Web Tokens (JWT) for authentication. 我有一个Symfony 3.2应用程序,它公开REST API并使用Json Web Tokens(JWT)进行身份验证。 I recently switched to using Symfony's Guard component. 我最近改用了Symfony的Guard组件。 Now my security.yml contains a firewall config section as follows (I'm using the Lexik JWT bundle 2.4.0, but this shouldn't matter): 现在我的security.yml包含一个防火墙配置部分,如下所示(我使用的是Lexik JWT软件包2.4.0,但这不重要):

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

Since I did this switch, I notice that every request is handled as if the user just logged in, ie a security.interactive_login event is fired. 由于我执行了此切换,因此我注意到每个请求都被处理,就像用户刚刚登录一样,即触发了security.interactive_login事件。 In the docs ( http://symfony.com/doc/current/components/security/authentication.html#authentication-events ) it states: 在文档( http://symfony.com/doc/current/components/security/authentication.html#authentication-events )中,它声明:

The security.interactive_login event is triggered after a user has actively logged into your website. 在用户主动登录您的网站后触发security.interactive_login事件。 It is important to distinguish this action from non-interactive authentication methods, such as: authentication based on a "remember me" cookie, authentication based on your session, authentication using a HTTP basic or HTTP digest header. 将此操作与非交互式身份验证方法区分开来非常重要,例如:基于“记住我”cookie的身份验证,基于会话的身份验证,使用HTTP基本身份验证或HTTP摘要头的身份验证。 You could listen on the security.interactive_login event, for example, in order to give your user a welcome flash message every time they log in. 例如,您可以监听security.interactive_login事件,以便每次登录时为您的用户提供欢迎闪存消息。

So I definitely don't expect this event for every request - I'd rather expect to get the security.authentication.success event on every request, as pointed out in the docs. 所以我绝对不希望每个请求都有这个事件 - 我宁愿在每个请求上得到security.authentication.success事件,正如文档中所指出的那样。

However, Symfony's GuardAuthenticatorHandler class dispatches the security.interactive_login event in its authenticateWithToken method, and this method is called by the GuardAuthenticationListener on every request. 但是,Symfony的GuardAuthenticatorHandler类在其authenticateWithToken方法中调度security.interactive_login事件,并且GuardAuthenticationListener在每个请求上调用此方法。 Is that a bug in Symfony, a misunderstanding on my side, or due to incorrect configuration? 这是Symfony的一个错误,我身边的误解,还是错误的配置?

(This is not a philosophical question - in my case it leads to the concrete problem that the last login time of the user is updated on every request, which does not make sense.) (这不是一个哲学问题 - 在我的情况下,它会导致具体的问题,即每次请求都会更新用户的上次登录时间,这是没有意义的。)

你应该改变这个

        stateless: false

I've come across your issue, because I've exactly the same problem. 我遇到过你的问题,因为我遇到了同样的问题。 My workaround is to add a attribute in the request object, right before return true in the supports method of the guard. 我的解决方法是在请求对象中添加一个属性,在guard的supports方法中返回true之前。

Example: 例:

public function supports(Request $request)
{
    ...

    $request->attributes->set('is_interactive_login', true);

    return true;
}

With this information you can check if it was a interactive login in the event listener 有了这些信息,您可以检查它是否是事件监听器中的交互式登录

Example: 例:

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

Better to subscribe to Events::JWT_CREATED event, as it's fired after authentication with credentials has been passed. 最好订阅Events::JWT_CREATED事件,因为它已通过凭证认证后被触发。

Example: 例:

<?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);
    }
}

I've faced with same trouble. 我遇到了同样的麻烦。 In my case I use Guard authentication for API requests. 就我而言,我对API请求使用Guard身份验证。 So I definitely don't like updating user's last_login after any API request. 所以我绝对不喜欢在任何API请求之后更新用户的last_login。

INTERACTIVE_LOGIN event is dispatched from here . INTERACTIVE_LOGIN事件从这里发送。

So my dirty hack is to add this definition to the services section of app's config: 所以我的脏黑客是将这个定义添加到app配置的services部分:

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

Warning 警告

Obvious drawback of this approach is that you broke change handler for all your app's guards. 这种方法的明显缺点是你 破坏 了所有应用程序防护的更改处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM