如何销毁 Symfony 2 中的所有会话

Posted

技术标签:

【中文标题】如何销毁 Symfony 2 中的所有会话【英文标题】:How to destroy all sessions in Symfony 2 【发布时间】:2015-05-02 13:44:33 【问题描述】:

更新

经过详细调查和咨询了一些专家,我突然想到,销毁会话的想法是错误的。更好的问题是——«如何强制所有用户退出»。

而且这个问题不应该从会话的角度来解决,这是一种相当低级的机制,而应该从安全组件的角度来解决。即使您删除了所有会话数据,它也会通过remember me cookie 与下一个用户请求一起重新创建。

稍后我会尝试提出这个问题的有效解决方案。

问题

我需要实现所谓的应用程序“锁定”功能,所以我需要一种方法来让所有用户退出 Symfony 2 应用程序(关闭所有活动会话)。

实现此功能的最佳方法是什么?

理想情况下,解决方案应该与所有可能的save-handlers 完全兼容。

看起来SessionHandlerInterface 没有提供这样做的方法。

【问题讨论】:

您可以根据您定义为过期的时间戳Invalidate the Session Based on its Age 部分执行类似this article 的操作。希望对您有所帮助 一个好主意@Matteo!看起来是迄今为止最好的,谢谢) 我会将其作为答案发布,如果您觉得有用,您可以评价 【参考方案1】:

编程方法应该是使用会话侦听器并在存在特定事件时使会话无效,例如数据库表中的标志/时间戳或类似事件。

如本文所述

Invalidate the Session Based on its Age

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class SessionListener


    /**
     * @var \Acme\DemoBundle\Service\SessionInvalidator
     */
    protected $sessionInvalidator;

    function __construct($sessionInvalidator)
    
        $this->sessionInvalidator=$sessionInvalidator;
    


    public function onKernelRequest(GetResponseEvent $event)
    
        if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) 
            return;
        

        $session = $event->getRequest()->getSession();
        $metadataBag = $session->getMetadataBag();

        $lastUsed = $metadataBag->getLastUsed();
        if ($lastUsed === null) 
            // the session was created just now
            return;
        

        // "last used" is a Unix timestamp

        if (! $this->sessionInvalidator->checkTimestampIsValid($lastUsed))
         $session->invalidate();
    

以及配置:

<service id="amce_security.verify_session_listener"
         class="Acme\DemoBundle\EventListener\SessionListener">
<argument type="service" id="acme.session_invalidator"/>
    <tag name="kernel.event_listener"
         event="kernel.request"
         priority="100"
         method="onKernelRequest" />
</service>

希望有帮助

【讨论】:

我已经实现了这种方法,但是,由于某种原因,调用$session-&gt;invalidate() 返回false,之后用户仍处于登录状态。你能详细说明一下吗?也许它与侦听器优先级和 PDO 初始化有关?我不确定。 您使用的是哪个版本的 sf?您可以尝试使用$this-&gt;securityContext-&gt;setToken(null); 使会话无效,或者此代码可能很快就会执行(即您的事件侦听器的优先级太高)。 ***.com/questions/18872721/… 也可能有用 我正在使用最新的 Symfony 2.6。我创建了一个 Gist 来向您展示我的代码:gist.github.com/slavafomin/960850217e40faaf4c99。我正在尝试使用$tokenStorage-&gt;setToken(null);,但这也无济于事。之后用户仍处于登录状态。 看起来它们被一分为二,只是表面上的改变。在内部,它应该和以前一样工作。我认为这些更改不会影响我遇到的行为。好的没问题。谢谢你的帮助!我将尝试自行进一步调查此问题,并在可能的情况下分享详细信息。【参考方案2】:

理想情况下,解决方案应该与所有可能的保存处理程序完全兼容。

为了完全独立于会话保存处理程序,我认为最好自己在会话“内部”实现这个。

当用户登录时,您将当前时间戳存储在他们的会话中(用户特定的登录时间戳)。

当您想要应用“锁定”时,您可以将当前时间戳(锁定时间戳)存储在其他位置,以便每个脚本实例都可以访问它。这可能是 memcached/shared memory 之类的东西,一个简单的文件(可能使用 touchfilemtime),或其他东西。

然后在每个页面请求上,检查存储在用户会话中的用户登录时间戳是否大于锁定时间戳。如果没有,用户在锁定发生之前登录 - 那么是时候删除他们的会话了。 当然,您在锁定期间拒绝新登录。

这样,您甚至可以允许用户在锁定结束后继续他们现有的会话(如果您愿意的话)——如果您选择不破坏他们的会话,而是在锁定期间简单地拒绝访问。为此,您要么完全删除锁定时间戳(删除 memcached/shmop 条目,删除文件),要么将其设置为过去的值(例如 0)。

您可能希望根据用户级别对锁定实施例外 - 管理员用户应该仍然可以使用该站点(如果只是禁用锁定,如果没有其他条件 - 至少为此他们需要无论锁定如何都可以登录)。

【讨论】:

刚才我看到 Matteo 在我输入答案时通过 cmets 提到了一个类似的建议……我还是把它留在这里。 感谢您抽出宝贵时间,非常感谢!【参考方案3】:

您可以在Symfony 2 Doc 之后将会话存储到数据库中。

基本上您需要在配置中执行以下操作:

framework:
    session:
        # ...
        handler_id: session.handler.pdo

services:
    session.handler.pdo:
        class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public:    false
        arguments:
            - "mysql:dbname=mydatabase"
            -  db_username: myuser, db_password: mypassword 

然后你可以从数据库中删除所有会话!

【讨论】:

感谢您的回复。我已经在使用 PDO 保存处理程序,这种方法看起来很诱人。但是,此功能将中断,如果稍后,我们将决定将保存处理程序更改为例如Memcache 或返回本机实现。有没有更好的多态方法? 我能想到的唯一其他方法是将所有给定的会话 ID 保存到一个特殊的数据库表中,删除注销系统的会话 ID,如果你需要关闭它,你只需可以只删除保存在一个表中的所有会话 ID。

以上是关于如何销毁 Symfony 2 中的所有会话的主要内容,如果未能解决你的问题,请参考以下文章

直接访问登录表单时未设置会话 cookie,导致 CSRF 令牌无效

Symfony2:使用 FOSUserBundle 时如何在控制器内获取用户对象?

Windows 安装 Symfony 2.2 框架 - 安装成功 !!

如何清空/销毁 Rails 中的会话?

如何在 symfony 2/3 中关闭会话?

如何通过 apache 清除 PHP $_SESSION?