Symfony Security 注销未清除 RememberMe 令牌

Posted

技术标签:

【中文标题】Symfony Security 注销未清除 RememberMe 令牌【英文标题】:Symfony Security logout not clearing RememberMe token 【发布时间】:2018-06-04 13:58:59 【问题描述】:

像这样使用带有security.yaml 的 Symfony 4:

encoders:
  App\Entity\User: sha256
providers:
    public_users:
      entity:
        class: App\Entity\User
        property: email
firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    main:
        pattern: ^/

        anonymous: ~

        form_login:
          login_path: login
          remember_me:    true

        remember_me:
            secret: "%kernel.secret%"
            name:  relevea_remember_me
            lifetime: 864000
            always_remember_me: false
            remember_me_parameter: user_login[stayConnected]

        logout:
            path: logout
            target: /about
            invalidate_session: false

access_control:
  -  path: ^/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY 

logoutoperation 没有清除 rememberMe 令牌。

我可以看到 LogoutListener (https://github.com/symfony/security/blob/master/Http/Firewall/LogoutListener.php) 在 RememberMeListener (https://github.com/symfony/security/blob/master/Http/Firewall/RememberMeListener.php) 之后被调用,因此对于 LogoutListener,令牌为空并且没有任何内容被清除:/

来自TraceableFirewallListener的听众名单:

Symfony\Component\Security\Http\Firewall\ChannelListener Symfony\Component\Security\Http\Firewall\ContextListener Symfony\Component\Security\Http\Firewall\LogoutListener

Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener Symfony\Component\Security\Http\Firewall\RememberMeListener

Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener Symfony\Component\Security\Http\Firewall\AccessListener

为什么注销监听器在其他人之前?

【问题讨论】:

【参考方案1】:

自 2013 年以来,这似乎是一个已知问题!

https://github.com/symfony/symfony/issues/7104

所以基本上,你不能从 RememberMe 令牌中注销:/

【讨论】:

您可以尝试创建自定义控制器并手动调用注销方法。 gist.github.com/sergeyfedotov/79e7cdf8ce4a960e5be1147417676ffb【参考方案2】:

您可以覆盖防火墙侦听器以调用注销侦听器,如下所示

security.firewall:
    class: AppBundle\Security\FirewallListener
    arguments:
       - '@security.firewall.map'
       - '@event_dispatcher'
       - '@security.logout_url_generator'
    tags:
       -  name: kernel.event_subscriber 


use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Http\Firewall\LogoutListener;
use Symfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;

class FirewallListener extends Firewall

    private $map;
    private $exceptionListeners;
    private $logoutUrlGenerator;
    private $dispatcher;

    public function __construct(FirewallMapInterface $map, EventDispatcherInterface $dispatcher, LogoutUrlGenerator $logoutUrlGenerator)
    
        $this->map = $map;
        $this->dispatcher = $dispatcher;
        $this->exceptionListeners = new \SplObjectStorage();
        $this->logoutUrlGenerator = $logoutUrlGenerator;

        parent::__construct($map, $dispatcher);
    

    /**
     * @inheritdoc
     */
    public function onKernelRequest(GetResponseEvent $event)
    
        if (!$event->isMasterRequest()) 
            return;
        
        if ($this->map instanceof FirewallMap && $config = $this->map->getFirewallConfig($event->getRequest())) 
            $this->logoutUrlGenerator->setCurrentFirewall($config->getName(), $config->getContext());
        

        // register listeners for this firewall
        list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest());
        if (null !== $exceptionListener) 
            $this->exceptionListeners[$event->getRequest()] = $exceptionListener;
            $exceptionListener->register($this->dispatcher);
        

        // initiate the listener chain
        $logoutListener = null;
        foreach ($listeners as $listener) 
            if ($listener instanceof LogoutListener) 
                $logoutListener = $listener;
                continue;
            

            $listener->handle($event);

            if ($event->hasResponse()) 
                break;
            
        

        if ($logoutListener) 
            $logoutListener->handle($event);
        
    

    /**
     * @inheritdoc
     */
    public function onKernelFinishRequest(FinishRequestEvent $event)
    
        if ($event->isMasterRequest()) 
            $this->logoutUrlGenerator->setCurrentFirewall(null);
        

        parent::onKernelFinishRequest($event);
    

【讨论】:

以上是关于Symfony Security 注销未清除 RememberMe 令牌的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 4:由管理员注销活动用户

[Symfony 5]注销后的确认信息

Symfony HWIOAuthBundle - 如何禁用自动注销?

无法从 Symfony 2 上的内存登录中注销

Symfony 自定义身份验证提供程序在请求重叠时注销

Symfony SecurityBundle:注销后如何保持在同一页面上?