每个帐户允许一个会话

时间:2017-07-18 13:03:38

标签: symfony

我搜索是否有一种简单的方法,只允许每个帐户使用Symfony 3一个会话?

目前,我使用PdoSessionHandler在数据库中存储会话,我在onSecurityInteractiveLogin事件上有一个监听器。当用户登录时,我在User对象中设置sessionId,并将其保存在数据库中。

现在,我想这样做: 当用户成功登录时,我也会停用上一个会话,但是如何停用其他会话?在Symfony中,我可以在实际会话中执行此操作,但不能用于其他...

否则,也许我可以处理一个SQL请求来删除前一个会话,但是,之前的用户会松开会话中存储的所有内容,我只想断开他。

另一种方式是反过来:对新用户说:“会话实际上是用你的登录打开的,请与其他机器断开连接。”但是如果用户只是关闭他的浏览器(没有点击退出)记住我的令牌后几秒钟或几分钟后回来,例如,他无法登录......并且必须等待几分钟。

如果有人有想法?

2 个答案:

答案 0 :(得分:1)

我必须在最近的一个项目中这样做。我是怎么做到的:

  1. 登录时,在会话和数据库中的用户记录中存储随机生成的令牌$x
  2. 在请求侦听器(kernel.request)中,比较当前登录令牌
     $user->getLoginToken(),在会话中使用令牌。如果不同,
  3. 使会话无效。
  4. 将新的重定向响应设置为错误页面。
  5. 致电$response->headers->clearCookie(....)以清除会话Cookie并记住我的Cookie。 (我有两个在我的参数中设置的名称,并将它们注入到这个请求监听器中。)

答案 1 :(得分:0)

最后我做到了:

  • 在SecurityInteractiveLoginListener中:成功登录后,我在用户记录(数据库)中添加一个带有会话ID的条目
  • 在每个请求(KernelRequestEvent)上,我检查用户的sessionId是否与数据库中的Record相同,如果是,则它是最后连接的用户,否则,已创建其他会话,然后我重定向用户on / logout,我添加了一个FlashMessage。

我只是关于组织的问题:我有2位听众,这是最好的方式吗?或者我需要在一个文件中进行订阅,并将2个事件分组,因为它具有相同的功能。

在我的security.yml

logout:
    path:   /logout
    target: /
    invalidate_session: false

invalidate_session:false,允许在注销时不破坏会话,然后我保留会话内容,并且当我强制用户注销时我可以添加一个flashbag。

我的服务声明:

app.event_listener.security_interactive_login:
    class: AppBundle\EventListener\SecurityInteractiveLoginListener
    arguments: ["@app.user_manager"]
    tags:
        - { name: kernel.event_listener, event: security.interactive_login }

app.event_listener.kernel_request:
    class: AppBundle\EventListener\KernelRequestListener
    arguments:
        - "@security.token_storage"
        - "@security.authorization_checker"
        - "@session"
        - "@router"
    tags:
        - { name: kernel.event_listener, event: kernel.request, priority: 0 }

SecurityInteractiveLoginListener:

<?php

namespace AppBundle\EventListener;

use AppBundle\Utils\UserManager;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class SecurityInteractiveLoginListener
{
    private $userManager;

    public function __construct(UserManager $userManager)
    {
        $this->userManager = $userManager;
    }

    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $request = $event->getRequest();
        $session = $request->getSession();
        $session->has('id'); // Just to fix a bug on Remember Me
        $user = $event->getAuthenticationToken()->getUser();

        // Set the session ID on user and save it in database
        $user->setSessionId($session->getId());
        $this->userManager->updateUser($user);
    }
}

KernelRequestListener:

<?php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;

class KernelRequestListener
{
    private $tokenStorage;
    private $authorizationChecker;
    private $session;
    private $router;

    public function __construct(
        TokenStorage $tokenStorage,
        AuthorizationChecker $authorizationChecker,
        Session $session,
        RouterInterface $router
    ) {
        $this->tokenStorage = $tokenStorage;
        $this->authorizationChecker = $authorizationChecker;
        $this->session = $session;
        $this->router = $router;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        if (!$event->isMasterRequest() || !$this->isUserLoggedIn()) {
            return;
        }

        $sessionId = $this->session->getId();
        $user = $this->tokenStorage->getToken()->getUser();

        // If the sessionId and the sessionId in database are equal: this is the latest connected user
        if ($sessionId === $user->getSessionId()) {
            return;
        }

        $this->session->getFlashBag()->add('danger', 'You have been logged out, because another person logged in whith your credentials.');
        $redirectUrl = $this->router->generate('logout');
        $response = new RedirectResponse($redirectUrl);

        $event->setResponse($response);
    }

    protected function isUserLoggedIn()
    {
        try {
            return $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED');
        } catch (AuthenticationCredentialsNotFoundException $exception) {
            // Ignoring this exception.
        }

        return false;
    }
}