【问题标题】:Symfony 4: Logout active user by adminSymfony 4:由管理员注销活动用户
【发布时间】: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 命令安装了用户登录处理。

标签: php symfony logout user-management


【解决方案1】:

这是杀死用户会话的好方法: 将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

    }
}

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

【讨论】:

  • 请注意,如果您只是在您的实体中设置一个 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();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 2016-07-23
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 2013-08-10
    • 2017-11-05
    相关资源
    最近更新 更多