【问题标题】:Log user out in Symfony 2 application when "remember me" is enabled启用“记住我”时,在 Symfony 2 应用程序中注销用户
【发布时间】: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 吗?

【问题讨论】:

    标签: php security symfony authentication logout


    【解决方案1】:

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

    警告

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

    解决方案

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

    1. 添加两个附加参数以将“会话”和“记住我”的 cookie 名称存储到您的 parameters.yml
    # parameters.yml
    
    parameters:
        session.name: SESS
        session.remember_me.name: LONGSESS
    
    1. 更新您的 config.yml 以使用会话名称的第一个参数:
    # config.yml
    
    framework:
        session:
            name: "%session.name%"
    
    1. 更新您的 security.yml 以使用第二个参数来记住我的会话名称:
    # security.yml
    
    security:
        firewalls:
            demo_secured_area:
                remember_me:
                    name: "%session.remember_me.name%"
    
    1. 这是您可以用来注销当前用户的代码:

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

    // 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 并在那里调用它的注销方法。
    猜你喜欢
    • 2015-03-03
    • 2016-02-16
    • 1970-01-01
    • 2022-07-14
    • 2017-11-05
    • 1970-01-01
    • 2015-05-03
    • 1970-01-01
    • 2019-09-20
    相关资源
    最近更新 更多