启用“记住我”时,在 Symfony 2 应用程序中注销用户

Posted

技术标签:

【中文标题】启用“记住我”时,在 Symfony 2 应用程序中注销用户【英文标题】:Log user out in Symfony 2 application when "remember me" is enabled 【发布时间】:2015-05-03 20:17:20 【问题描述】:

我正在寻找一种让用户退出 Symfony 2 应用程序的方法,但找不到正确的方法。

我尝试了这里描述的方法: Symfony2: how to log user out manually in controller?

$this->get('security.context')->setToken(null);
$this->get('request')->getSession()->invalidate();

当“remember me”被禁用时它工作正常,但是,当我启用它时,它就不起作用了。看起来用户已被此 cookie 自动重新验证。

remember_me:
    key:      "%secret%"
    lifetime: 31536000
    path:     /
    domain:   ~
    always_remember_me: true

从 Symfony 2 应用程序中注销用户的正确方法是什么?我还需要从服务器端删除这个 cookie 吗?

【问题讨论】:

【参考方案1】:

感谢@nifr 我能够解决这个问题。这是手动让用户退出 Symfony 2 应用程序的防弹分步指南。

警告

Symfony 已经实现了注销用户和删除 cookie 的功能。有一个 LogoutListener 将这些操作委托给几个注销处理程序:CookieClearingLogoutHandlerSessionLogoutHandler。我认为最好的做法是调用这些处理程序,而不是自己实现这样的低级逻辑。但是,我找不到这样做的方法。

解决方案

此解决方案适用于 Symfony 2.6。区别在于security.token_storage

    添加两个附加参数以将“会话”和“记住我”的 cookie 名称存储到您的 parameters.yml
# parameters.yml

parameters:
    session.name: SESS
    session.remember_me.name: LONGSESS
    更新您的 config.yml 以使用会话名称的第一个参数:
# config.yml

framework:
    session:
        name: "%session.name%"
    更新您的 security.yml 以使用第二个参数来记住我的会话名称:
# security.yml

security:
    firewalls:
        demo_secured_area:
            remember_me:
                name: "%session.remember_me.name%"
    这是您可以用来注销当前用户的代码:

如果需要,您可以在内核事件侦听器中使用此类代码。

// SomeController.php

/**
 * @Route("/terminate", name="app.terminate")
 */
public function terminateAction()

    // Logging user out.
    $this->get('security.token_storage')->setToken(null);

    // Invalidating the session.
    $session = $this->get('request')->getSession();
    $session->invalidate();

    // Redirecting user to login page in the end.
    $response = $this->redirectToRoute('app.login');

    // Clearing the cookies.
    $cookieNames = [
        $this->container->getParameter('session.name'),
        $this->container->getParameter('session.remember_me.name'),
    ];
    foreach ($cookieNames as $cookieName) 
        $response->headers->clearCookie($cookieName);
    

    return $response;

这是内核事件监听器的实现,它将根据实体属性强制用户注销:Logging user out of Symfony 2 application using kernel event listener。

希望对你有帮助。

【讨论】:

请注意,如果您在session 配置中使用domain 设置,则必须将此值设置为clearCookie 中的第三个参数。 您应该在清除 cookie 后使用 $response->send(); 以使其正常工作。【参考方案2】:

您可能必须显式调用会话存储的save() (Documentation) 方法。

强制保存并关闭会话。

您还可以通过响应标头请求删除 session- 和/或 remember_me-cookie。

session-cookie 的名称配置为容器参数 framework.session.name,默认为 php.ini 中的 session.name 值。

$cookieName = $this->container->getParameter('framework.session.name');
$response->headers->clearCookie( $cookieName );

remember_me-cookie 的名称可以在您的security 配置中进行配置。

security:
    firewalls:
        your_firewall:
            remember_me: 
                name: neverforget # <- cookie-name

【讨论】:

您清除缓存了吗?请清除您的浏览器缓存/cookies 和 symfony 的缓存 - 应该可以工作。否则你的ini_get('session.name'); 设置是什么? 手动删除 cookie 有助于解决问题,谢谢! 我只是好奇,是否可以将其委托给 Symfony?我认为这将是一个更好的方法。 把这个委托给 symfony 是什么意思? ...创建一个响应侦听器,如果满足某些条件则执行注销? 不,我的意思是,Symfony 已经实现了注销用户和终止 cookie 的功能。有一个 LogoutListener 将这些操作委托给几个注销处理程序:CookieClearingLogoutHandlerSessionLogoutHandler。我认为最好的做法是调用这些处理程序,而不是自己实现这样的低级逻辑。【参考方案3】:

@Slava Fomin II

Symfony 已经实现了注销用户和删除 cookie 的功能。有一个 LogoutListener 将这些操作委托给几个注销处理程序:CookieClearingLogoutHandler 和 SessionLogoutHandler。我认为最好的做法是调用这些处理程序,而不是自己实现这样的低级逻辑。但是,我找不到这样做的方法。

为什么不简单地创建一个调用它们的服务?

我查看了Symfony\Component\Security\Http\Firewall\LogoutListener 并测试了他在注销期间调用了 2 个服务(Symfony 3.2.9)。

$tokenBasedRememberMeServices顺便删除了remember-me cookie。

<?php declare(strict_types=1);

namespace MyProject\Security\Listener;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Logout\SessionLogoutHandler;
use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices;

final class LogoutListener

    private $sessionLogoutHandler;
    private $tokenBasedRememberMeServices;
    private $defaultLogoutSuccessHandler;
    private $tokenStorage;

    public function __construct(
        SessionLogoutHandler $sessionLogoutHandler,
        TokenBasedRememberMeServices $tokenBasedRememberMeServices,
        DefaultLogoutSuccessHandler $defaultLogoutSuccessHandler,
        TokenStorage $tokenStorage
    )
    
        $this->sessionLogoutHandler = $sessionLogoutHandler;
        $this->tokenBasedRememberMeServices = $tokenBasedRememberMeServices;
        $this->defaultLogoutSuccessHandler = $defaultLogoutSuccessHandler;
        $this->tokenStorage = $tokenStorage;
    

    public function logout(Request $request): void
    
        $token = $this->tokenStorage->getToken();
        $response = $this->defaultLogoutSuccessHandler->onLogoutSuccess($request);
        $this->sessionLogoutHandler->logout($request, $response, $token);
        $this->tokenBasedRememberMeServices->logout($request, $response, $token);
    


【讨论】:

您好,感谢您的回答。然而,这个问题是很久以前提出的,可能(至少我希望如此)Symfony 在新版本中改进了它的身份验证处理架构。我目前没有使用 Symfony,因为我已经切换到 Node.js,所以我无法验证您的答案。对不起!但我希望它会对某人有所帮助。 对于 Symfony 3.2.9 我没有找到任何提供的解决方案。希望我能帮助别人解决我的问题。 我的第一个问题是,这个解决方案是否仍然适用于 Symfony 4?其次,使用 Listener/Subscriber 的任何具体原因?为什么不直接使用常规服务,将其注入 Controller 并在那里调用它的注销方法。

以上是关于启用“记住我”时,在 Symfony 2 应用程序中注销用户的主要内容,如果未能解决你的问题,请参考以下文章

记住我功能在 Symfony2 中不起作用

为啥我看到多个 set-cookie 标头?

在 Symfony 应用程序/控制台中启用 Doctrine DBAL 命令

如何分别为每一个定义“记住我”? Symfony 5

在 symfony 中为 @babel/plugin-proposal-class-properties 启用 classProperties

Symfony2、Sonata、UserBundle:当用户启用更新时发送电子邮件