我的Symfony 4.2应用程序中的管理员用户应能够注销另一个(非管理员)用户。我根据Symfony安全捆绑包(https://symfony.com/doc/current/security/form_login_setup.html)创建了一个用户登录系统。
现在,我正在构建一个管理仪表盘,其中必须列出所有用户的在线状态(最后活动)。
是否有推荐的方法列出活动用户并在需要时终止其会话?
我读了一些这样的帖子:Symfony how to return all logged in Active Users。但是答案有点老了,只是列出了活跃用户。
答案 0 :(得分:0)
正确的方法是将用户会话存储在数据库中。
https://symfony.com/doc/current/doctrine/pdo_session_storage.html(这是数据库表的创建语法。还要在表中添加一个user_id)
在framework.yml中添加Pdo会话处理程序。
session:
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
cookie_secure: auto
cookie_samesite: lax
在service.yml中添加一个侦听器并注册会话处理程序
# Handlers
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
arguments:
- !service { class: PDO, factory: 'database_connection:getWrappedConnection' }
- { lock_mode: 1 }
# Listeners
App\Listener\SessionListener:
tags:
- {name: kernel.event_listener, event: kernel.request, method: onRequestListener}
在其中创建新的侦听器
class SessionListener
{
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var SessionInterface
*/
private $session;
public function __construct(
TokenStorageInterface $tokenStorage,
EntityManagerInterface $em,
SessionInterface $session
) {
$this->tokenStorage = $tokenStorage;
$this->em = $em;
$this->session = $session;
}
public function onRequestListener(GetResponseEvent $event): void
{
// If its not te master request or token is null
if (!$event->isMasterRequest() || $this->tokenStorage->getToken() === null) {
return;
}
/** @var User $user */
$user = $this->tokenStorage->getToken()->getUser();
// Check if user is logged in
if (!$user instanceof User) {
return;
}
$connection = $this->em->getConnection();
try {
$stmt = $connection->prepare('UPDATE `sessions` SET `user_id` = :userId WHERE `sess_id` = :sessionId');
$stmt->execute([
'userId' => $user->getId(),
'sessionId' => $this->session->getId(),
]);
} catch (DBALException $e) {
}
}
}
现在只需删除该用户的会话即可。
/**
* @var EntityManagerInterface
*/
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function delete(User $user): void
{
$sessions = $this->em->getRepository(Session::class)->findBy([
'user' => $user,
]);
foreach ($sessions as $session) {
$this->em->remove($session);
}
$this->em->flush();
}
答案 1 :(得分:0)
这是杀死用户会话的好方法:
将EventListener
与onKernelRequest
事件一起使用。在您的主要代码中:public function onKernelRequest(KernelEvent $event)
$request = $event->getRequest();
$token = $this->container->get('security.token_storage')->getToken();
if ($token === null) { // somehow
return;
}
if ($token->getUser()->isLocked() === true) {
// you must implement a boolean flag on your user Entities, which the admins can set to false
$this->container->get('security.token_storage')->setToken(); // default is null, therefore null
$request->getSession()->invalidate(); // these lines will invalidate user session on next request
return;
}
现在,再问另一个问题:如何列出具有在线状态的用户?容易,您的用户实体应该实现另一个布尔标志,例如isOnline
(带有getter和setter)。
接下来,您应该创建一个LoginListener
(无需实现任何接口)。在您的主要代码中:
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) {
$user = $event->getAuthenticationToken()->getUser();
if ($user instanceof UserInterface) {
// set isOnline flag === true
// you will need to fetch the $user with the EntityManager ($this->em)
// make sure it exists, set the flag and then
$this->em->flush();
}
}
您的第三个事件应该是LogoutListener
,您将在其中设置isOnline flag === false
Symfony在用户请求注销时调用LogoutListener(作为处理程序)。 但是您可以自己编写:
class LogoutListener implements LogoutHandlerInterface {
public function logout(Request $request, Response $response, TokenInterface $token): void
{
$user = $token->getUser();
if (!$user instanceof UserInterface) { /** return if user is somehow anonymous
* this should not happen here, unless... reasons */
return;
}
// else
$username = $user->getUsername(); // each user class must implement getUsername()
// get the entity Manager ($this->em, injected in your constructor)
// get your User repository
$repository = $this->em->getRepository(MyUser::class);
$user = $repository->findOneBy(['username' => $username]); // find one by username
$user->setIsOnline(false);
$this->em->flush(); // done, you've recorded a logout
}
}
希望这会有所帮助。运气好的话,它会的。干杯! :-)