我有一个Symfony 3.2应用程序,它公开REST API并使用Json Web Tokens(JWT)进行身份验证。我最近改用了Symfony的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
事件。在文档(http://symfony.com/doc/current/components/security/authentication.html#authentication-events)中,它指出:
在用户主动登录您的网站后触发security.interactive_login事件。将此操作与非交互式身份验证方法区分开来非常重要,例如:基于“记住我”cookie的身份验证,基于会话的身份验证,使用HTTP基本身份验证或HTTP摘要头的身份验证。例如,您可以监听security.interactive_login事件,以便每次登录时为您的用户提供欢迎闪存消息。
所以我绝对不希望每个请求都有这个事件 - 我希望在每个请求中得到security.authentication.success
事件,正如文档中所指出的那样。
然而,Symfony的GuardAuthenticatorHandler
类在其security.interactive_login
方法中调度authenticateWithToken
事件,并且GuardAuthenticationListener
在每次请求时调用此方法。这是Symfony的一个错误,我身边的误解,还是错误的配置?
(这不是一个哲学问题 - 在我的情况下,它会导致具体问题,即每次请求都会更新用户的上次登录时间,这是没有意义的。)
你应该改变这个
stateless: false
我遇到过你的问题,因为我遇到了同样的问题。我的解决方法是在请求对象中添加一个属性,在guard的supports方法中返回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
}
}
最好订阅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);
}
}
我遇到了同样的麻烦。就我而言,我对API请求使用Guard身份验证。所以我绝对不喜欢在任何API请求之后更新用户的last_login。
INTERACTIVE_LOGIN事件从here发送。
所以我的脏黑客是将这个定义添加到app配置的services
部分:
security.authentication.guard_handler:
class: Symfony\Component\Security\Guard\GuardAuthenticatorHandler
arguments:
$eventDispatcher: ~
这种方法的明显缺点是你 打破 更改所有应用程序警卫的处理程序。