Symfony 4:通过管理员注销活动用户

时间:2019-07-25 07:57:17

标签: php symfony logout user-management

我的Symfony 4.2应用程序中的管理员用户应能够注销另一个(非管理员)用户。我根据Symfony安全捆绑包(https://symfony.com/doc/current/security/form_login_setup.html)创建了一个用户登录系统。

现在,我正在构建一个管理仪表盘,其中必须列出所有用户的在线状态(最后活动)。

是否有推荐的方法列出活动用户并在需要时终止其会话?

我读了一些这样的帖子:Symfony how to return all logged in Active Users。但是答案有点老了,只是列出了活跃用户。

2 个答案:

答案 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)

这是杀死用户会话的好方法: 将EventListeneronKernelRequest事件一起使用。在您的主要代码中: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

    }
}

希望这会有所帮助。运气好的话,它会的。干杯! :-)