我搜索是否有一种简单的方法,只允许每个帐户使用Symfony 3一个会话?
目前,我使用PdoSessionHandler
在数据库中存储会话,我在onSecurityInteractiveLogin
事件上有一个监听器。当用户登录时,我在User对象中设置sessionId,并将其保存在数据库中。
现在,我想这样做: 当用户成功登录时,我也会停用上一个会话,但是如何停用其他会话?在Symfony中,我可以在实际会话中执行此操作,但不能用于其他...
否则,也许我可以处理一个SQL请求来删除前一个会话,但是,之前的用户会松开会话中存储的所有内容,我只想断开他。
另一种方式是反过来:对新用户说:“会话实际上是用你的登录打开的,请与其他机器断开连接。”但是如果用户只是关闭他的浏览器(没有点击退出)记住我的令牌后几秒钟或几分钟后回来,例如,他无法登录......并且必须等待几分钟。
如果有人有想法?
答案 0 :(得分:1)
我必须在最近的一个项目中这样做。我是怎么做到的:
$x
。kernel.request
)中,比较当前登录令牌$user->getLoginToken()
,在会话中使用令牌。如果不同,$response->headers->clearCookie(....)
以清除会话Cookie并记住我的Cookie。 (我有两个在我的参数中设置的名称,并将它们注入到这个请求监听器中。)答案 1 :(得分:0)
最后我做到了:
我只是关于组织的问题:我有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;
}
}