如何销毁 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->invalidate()
返回false
,之后用户仍处于登录状态。你能详细说明一下吗?也许它与侦听器优先级和 PDO 初始化有关?我不确定。
您使用的是哪个版本的 sf?您可以尝试使用$this->securityContext->setToken(null);
使会话无效,或者此代码可能很快就会执行(即您的事件侦听器的优先级太高)。
***.com/questions/18872721/… 也可能有用
我正在使用最新的 Symfony 2.6。我创建了一个 Gist 来向您展示我的代码:gist.github.com/slavafomin/960850217e40faaf4c99。我正在尝试使用$tokenStorage->setToken(null);
,但这也无济于事。之后用户仍处于登录状态。
看起来它们被一分为二,只是表面上的改变。在内部,它应该和以前一样工作。我认为这些更改不会影响我遇到的行为。好的没问题。谢谢你的帮助!我将尝试自行进一步调查此问题,并在可能的情况下分享详细信息。【参考方案2】:
理想情况下,解决方案应该与所有可能的保存处理程序完全兼容。
为了完全独立于会话保存处理程序,我认为最好自己在会话“内部”实现这个。
当用户登录时,您将当前时间戳存储在他们的会话中(用户特定的登录时间戳)。
当您想要应用“锁定”时,您可以将当前时间戳(锁定时间戳)存储在其他位置,以便每个脚本实例都可以访问它。这可能是 memcached/shared memory 之类的东西,一个简单的文件(可能使用 touch
和 filemtime
),或其他东西。
然后在每个页面请求上,检查存储在用户会话中的用户登录时间戳是否大于锁定时间戳。如果没有,用户在锁定发生之前登录 - 那么是时候删除他们的会话了。 当然,您在锁定期间拒绝新登录。
这样,您甚至可以允许用户在锁定结束后继续他们现有的会话(如果您愿意的话)——如果您选择不破坏他们的会话,而是在锁定期间简单地拒绝访问。为此,您要么完全删除锁定时间戳(删除 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 时如何在控制器内获取用户对象?