Symfony 在 SwitchUserListener 中注入 EntityManager

Symfony inject EntityManager in SwitchUserListener

我的问题是如何在已经有 9 个参数的 SwitchUserListener 中注入实体管理器。

我有一个自定义切换用户流程,我需要在会话中设置与 _switch_user 参数 (?_switch_user=user1&external_client_id=1) 一起传递的 ExternalClient。在设置之前,我必须先从数据库中获取 ExternalClient。

我在parameters.yml中添加了

security.authentication.switchuser_listener.class: App\Bundle\Listener\SwitchUserListener

对于 App\Bundle\Listener\SwitchUserListener 的内容,我使用了 Symfony SwitchUserListener Symfony\Component\Security\Http\Firewall\SwitchUserListener.php

一切正常,当我从侦听器中的请求变量中获取 external_client_id 参数时,它被填充。但是我似乎无法访问实体管理器。

我尝试过的事情:

  1. services.yml

    中添加装饰器
    app.decorating_switch_user:
      class:     App\Bundle\Listener\SwitchUserListener
      decorates: security.authentication.switchuser_listener
      arguments: ['@app.decorating_switch_user.inner', '@doctrine.orm.entity_manager']
      public:    false
    
  2. 覆盖 services.yml

    中的父依赖项
    security.authentication.switchuser_listener:
      abstract:  true
    
    test:
      class: "%security.authentication.switchuser_listener.class%"
      parent: security.authentication.switchuser_listener
      public: false
      # appends the '@doctrine.orm.entity_manager' argument to the parent
      # argument list
      arguments: ['@doctrine.orm.entity_manager']
    
  3. 改为监听 SwitchUserEvent

     app.switch_user_listener:
       class: App\Bundle\Listener\SwitchUserListener
       tags:
          - { name: kernel.event_listener, event: security.switch_user, method: onSwitchUser }
    

    在这里,我将 'App\Bundle\Listener\SwitchUserListener' 的内容替换为:

    class SwitchUserListener
    {
        public function onSwitchUser(SwitchUserEvent $event)
        {
            $request = $event->getRequest();
            echo "<pre>";
            dump($externalClientId = $request->get('external_client_id'));
            echo "</pre>";
            exit;
        }
    }
    

    通过这次尝试,我也得到了 external_client_id,但我不知道如何注入实体管理器。即使我这样做了,我也无法获得发起 _switch_user 请求的原始用户。 SwitchUserEvent 只能访问 getTargetUser() 方法。

结论:

如果有人对此主题有经验并愿意分享,那就太好了。理想情况下,我会将实体管理器服务添加到 __construct 函数的前 9 个参数中。我正在扩展 class 就像 Matt 在这里做的那样:Symfony2: Making impersonating a user more friendly

您可以按如下方式覆盖该服务。您可能需要查看 up/change 服务参数的具体顺序,因为它在 symfony 版本之间发生了变化。一些像 $providerKey 这样的参数可以留空,因为它们会被 symfony 自动设为 changed/injected。

为了节省一些编码时间,如果您使用 setter 注入,则不需要重写构造函数。

看看 Symfony 的默认 SwitchUserListener(切换到您的应用程序中使用的 tag/version)将有助于实现您的新 handle 方法。

# app/config/services.yml
services:
  # [..]
  security.authentication.switchuser_listener:
    class: 'Your\Namespace\SwitchUserListener'
    public: false
    abstract: true
    arguments: 
      - '@security.context'
      - ~
      - '@security.user_checker' 
      - ~
      - '@security.access.decision_manager' 
      - '@?logger' 
      - '_switch_user' 
      - ~
      - '@?event_dispatcher' 
      - ~
    calls:
      - [ 'setEntityManager', [ '@doctrine.orm.entity_manager' ]]
    tags:
      - { name: monolog.logger, channel: security }

现在您的 SwitchUserListener 可能如下所示:

namespace Your\Namespace;

use Symfony\Component\Security\Http\Firewall\SwitchUserListener as DefaultListener;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class SwitchUserListener extends DefaultListener
{
    /** @var EntityManagerInterface */
    protected $em;

    public function setEntityManager(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    /**
     * Handles the switch to another user.
     *
     * @throws \LogicException if switching to a user failed
     */
     public function handle(GetResponseEvent $event)
     {
          // Do your custom switching logic here
     }
}

别忘了清除缓存!