Symfony2:注入@ security.context以获取当前用户。如何避免“ServiceCircularReferenceException”?注入整个容器?

时间:2014-03-02 13:47:52

标签: security symfony dependency-injection doctrine-orm

来自this post并在解决问题之后,我处于另一个问题/安全问题/问题中。

正如你可能在另一篇文章中看到的那样,我正在尝试在监听器中注入安全上下文,但是如果我保持代码完整而没有触摸我得到了这个错误:

  

ServiceCircularReferenceException:检测到循环引用   服务“doctrine.orm.default_entity_manager”

所以,阅读和研究我找到了一个解决方案,但我不清楚它是否是正确的,或者它对我的应用程序是否安全。所以这就是我所做的:

而不是注入[@security.context]我做了这个:

services:
    orderhascomment.listener:
        class: PL\OrderBundle\Listener\OrderHasCommentListener
        arguments: [@service_container]
        tags:
            - { name: doctrine.event_listener, event: prePersist, method: onPrePersist }

我的听众OrderHasCommentListener.php如下:

namespace PL\OrderBundle\Listener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;

class OrderHasCommentListener {

    protected $container;

    public function __construct(ContainerInterface $container = null) {
        $this->container = $container;
    }

    /**
     *
     * @param LifecycleEventArgs $args 
     */
    public function onPrePersist(LifecycleEventArgs $args) {

        $entity = $args->getEntity();
        $user = $this->container->get('security.context')->getToken()->getUser();
        $entity->setUser($user);
    }

}

这是正确的方法吗?还是存在另一个?我认为注入整个容器是一个坏主意,因为我只需要安全上下文,那么解决方案是什么? (https://insight.sensiolabs.com/what-we-analyse

尝试在服务中转换UserCallable

我正在尝试按照here的说明转换服务中的UserCallable并查看DoctrineBehaviors orm-services.yml file,并了解他们是如何在BlameableListener处理的,但我因为我收到这个错误所以无法让它工作:

  

ContextErrorException:Catchable Fatal Error:传递给的参数1   PL \ OrderBundle \ Listener \ OrderHasCommentListener :: __ construct()必须是   可调用的,给定的字符串

这就是我在app/config/config.yml的定义:

services:
    orderhascomment.listener:
        class: PL\OrderBundle\Listener\OrderHasCommentListener
        arguments: 
            - user_callable
        tags:
            - { name: doctrine.event_listener, event: prePersist, method: onPrePersist }
    user_callable:
        class: PL\OrderBundle\Util\UserCallable
        arguments:
            - "@service_container"
        public:  false

这就是我在__construct()文件中传递给OrderHasCommentListener.php函数的方式:

/**
 * @param UserCallableInterface $user_callable 
 * */
public function __construct(callable $user_callable = null) {
    $this->userCallable = $user_callable;
}

有什么问题?

1 个答案:

答案 0 :(得分:9)

将整个容器直接注入列表器可能是一种可行的解决方案......但我们可以做得更好:)

注入一个返回当前用户的UserCallable

这样,您可以更清楚地表达依赖关系的真正目的,而不会在侦听器和容器(-interface)之间引入硬依赖关系。一个例子是......

Knp\DoctrineBehaviors\ORM\Blameable\UserCallable

可以通过创建一个接口并在侦听器中使用类型提示来进一步改进此特定示例。如果您打算重新使用监听器,那么这样可以更轻松地进行交换。

接口:

namespace Acme\Common;

interface UserCallableInterface
{
    /**
     * @return \Symfony\Component\Security\Core\User\UserInterface
     */
    public function getCurrentUser();
}

namespace Acme\Common;

use Symfony\Component\Security\Core\User\UserInterface;

interface TrackableInterface
{
    /**
     * @param UserInterface $user
     */
    public function setUser(UserInterface $user);
}

UserCallable:

namespace Acme\Util;

use Acme\Common\UserCallableInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class UserCallable implements UserCallableInterface
{
   /** @var ContainerInterface **/
   protected $container;

   /** 
    * @param ContainerInterface $container
    */
   public function __construct(ContainerInterface $container)
   {
      $this->container = $container;
   }

   /**
    * @{inheritdoc}
    */
   public function getCurrentUser()
   {
      return $this->container->get('security.context')->getToken()->getUser() ?: false;
   }

听众:

use Acme\Common\UserCallableInterface;
use Acme\Common\TrackableInterface;
use Doctrine\Common\EventArgs;

class Listener
{
    /** @var UserCallableInterface **/
    protected $userCallable;

    /** 
     * @param UserCallableInterface $user_callable 
     **/    
    public function __construct(UserCallableInterface $user_callable)
    {
       $this->userCallable = $user_callable;
    }

    /** 
     * @param EventArgs $args 
     **/
    public function onPrePersist(EventArgs $args)
    {
       $entity = $args->getEntity();

       if ( !($entity instanceof TrackableInterface) ) {
           return;
       }

       if ( !($user = $this->userCallable->getCurrentUser())) {
           return;
       }

       $entity->setUser($user);
    }      
}