【问题标题】:How to close all sessions for a user in Symfony 2.7?如何在 Symfony 2.7 中关闭用户的所有会话?
【发布时间】:2016-04-23 18:16:55
【问题描述】:

用户更改密码后(在恢复密码操作中),我需要使连接到该用户的所有会话无效(他可能在多个浏览器/设备上登录)。因此,在我用新密码将用户保存在数据库中之后,我需要关闭该用户在不同浏览器/设备中可能处于活动状态的所有会话。我试过这个:

$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);

也试过了:

$this->get('security.token_storage')->getToken()->setAuthenticated(false);

还有这个:

$this->get('security.token_storage')->setToken(null);

提前致谢!

我已将此添加到我的用户类中:

class User implements UserInterface, EquatableInterface, \Serializable{
  // properties and other methods

  public function isEqualTo(UserInterface $user){
    if ($user->getPassword() !== $this->getPassword()){
        return false;
    }
    if ($user->getEmail() !== $this->getEmail()){
        return false;
    }
    if ($user->getRoles() !== $this->getRoles()){
        return false;
    }
    return true;
  }


  /** @see \Serializable::serialize() */
  public function serialize()
  {
    return serialize(array(
        $this->id,
        $this->email,
        $this->password,
        // see section on salt below
        // $this->salt,
    ));
  }

  /** @see \Serializable::unserialize() */
  public function unserialize($serialized)
  {
    list (
        $this->id,
        $this->email,
        $this->password,
        // see section on salt below
        // $this->salt
    ) = unserialize($serialized);
  }
}

【问题讨论】:

  • 赞成,因为我不完全确定自己 - 阅读此内容是因为您想使连接到用户的所有会话无效(可以在多个浏览器/设备上登录)
  • 是的,这就是我需要的。我将重写问题,以便更清楚。

标签: php symfony symfony-2.7


【解决方案1】:

您需要跟踪该用户打开的每个会话。由于他可能在多个浏览器/设备上登录,因此他可能会使用不同的会话。

一个简单的方法是将会话ID的引用与用户ID一起保存,这样您就可以获得用户的所有sessid。

namespace AppBundle\Security;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;

class LoginListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
        );
    }

    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();
        $session = $event->getRequest()->getSession();

        /*
         * Save $user->getId() and $session->getId() somewhere
         * or update it if already exists.
         */
    }
}

然后注册一个security.interactive_login 事件,每次用户登录时都会触发该事件。然后您可以注册监听器

<service id="app_bundle.security.login_listener" class="AppBundle\Security\LoginListener.php">
    <tag name="kernel.event_subscriber" />
</service> 

之后,当您想撤销用户的所有会话时,您需要做的就是检索该用户的所有会话 ID,循环并使用

销毁它们
$session = new Session();
$session->setId($sessid);
$session->start();
$session->invalidate();

【讨论】:

  • 刚刚尝试并得到Cannot change ID of an active session。例如,如果您实际登录,您似乎无法使第三方会话无效。
  • 你应该创建一个新的 Session 对象。我们可以看看你的代码吗?
  • $session = new Session();$session = $request-&gt;getSession(); 之间也是如此。您可以使用 session_id($sessid); 来欺骗它,而不是使用 -&gt;setId(); :)
  • 这是一个很好的方法,但是不将会话存储在数据库中(安全方面)而是在 Redis 中存储呢?另外,在 redis 上,您可以为保存的条目设置超时。
【解决方案2】:

Sebcar,

这是 Symfony 安全本身的一个错误: Bug explanation to be review First Bug

所以你必须重写抽象令牌

【讨论】:

    【解决方案3】:

    根据默认设置中的documentation,您应该已经发生了。

    例如,如果 2 个用户对象上的用户名由于某种原因不匹配,则出于安全原因,该用户将被注销。 (...) Symfony 还使用用户名、salt 和密码来验证用户在请求之间没有更改

    我不知道您的用户是如何配置的,但您可能需要实现EquatableInterface

    【讨论】:

    • 我已按照您的建议更新了我的代码,但仍然无法正常工作,可能是我遗漏了一些东西。谢谢!
    • 看起来不错,不知道为什么它不起作用。可能值得一点调试,看看isEqualTo 是否按预期返回 false。也许您可以引入一些额外的列version,当密码更新并序列化时会计数。
    【解决方案4】:

    试试$session-&gt;clear();

    查看documentation

    【讨论】:

    • 会话将一个浏览器与服务器“通信”。在这种情况下,用户可以登录多个浏览器/设备。所以我需要关闭属于该用户的所有会话。如果我错过了第一次写问题,我很抱歉。
    • 好吧,对不起,我错过了理解^^
    猜你喜欢
    • 2010-12-18
    • 2015-07-07
    • 2017-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多