Symfony 4:由管理员注销活动用户
Posted
技术标签:
【中文标题】Symfony 4:由管理员注销活动用户【英文标题】:Symfony 4: Logout active user by admin 【发布时间】:2019-12-03 10:30:57 【问题描述】:我的 Symfony 4.2 应用程序中的管理员用户应该能够注销另一个(非管理员)用户。我根据 Symfony 安全包 (https://symfony.com/doc/current/security/form_login_setup.html) 创建了一个用户登录系统。
现在我正在构建一个管理仪表板,其中必须列出所有用户的在线状态(上次活动)。
是否有推荐的方法来列出活跃用户并在需要时终止他们的会话?
我读过一些这样的帖子:Symfony how to return all logged in Active Users。但答案有点老了,只是列出了活跃用户。
【问题讨论】:
您使用的是什么会话处理程序? 好问题。我没有为会话处理更改或安装任何东西。因此,如果有默认值,我会说“默认值”。我通过 make:user 命令安装了用户登录处理。 【参考方案1】:这是杀死用户会话的好方法:
将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
希望这会有所帮助。运气好的话,它会的。干杯! :-)
【讨论】:
请注意,如果您只是在您的实体中设置一个 DateTimeInterface(例如 $lastLogin),它将无济于事。您确实需要两个布尔值,例如:1/ $locked(管理员请求锁定帐户)和 2/ $online(用户当前在线 - 以便我们知道是否需要根据管理员请求将他/她注销)。祝你好运【参考方案2】:正确的做法是将用户会话存储在数据库中。
https://symfony.com/doc/current/doctrine/pdo_session_storage.html(这里是数据库表的创建语法,还要在表中添加一个user_id)
在 framework.yml 中添加 Pdo Session Handler。
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();
【讨论】:
以上是关于Symfony 4:由管理员注销活动用户的主要内容,如果未能解决你的问题,请参考以下文章