允许每个帐户一个会话

mpi*_*iot 3 symfony

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

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

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

否则,也许我可以处理 SQL 请求来删除先前的会话,但是随后,先前的用户丢失了会话中存储的所有内容,我只想断开他的连接。

另一种方法是相反的:对新用户说:“会话实际上是通过您的登录打开的,请断开与另一台计算机的连接。”,但如果用户只是关闭浏览器(不单击注销)并返回一些秒/分钟后使用记住我令牌,例如,他无法登录......并且必须等待几分钟。

如果有人有想法?

mpi*_*iot 5

最后我做到了:

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

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

在我的 security.yml 中

logout:
    path:   /logout
    target: /
    invalidate_session: false
Run Code Online (Sandbox Code Playgroud)

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 }
Run Code Online (Sandbox Code Playgroud)

安全交互式登录监听器:

<?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);
    }
}
Run Code Online (Sandbox Code Playgroud)

内核请求监听器:

<?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;
    }
}
Run Code Online (Sandbox Code Playgroud)