4

我正在使用 Gedmo Loggable 来跟踪用户对实体所做的更改。默认情况下不会为每次更改存储用户名,但有必要将其提供给侦听器。

这方面的一个例子,可以在 Loggable 的文档中找到:

  $loggableListener = new Gedmo\Loggable\LoggableListener;
  $loggableListener->setAnnotationReader($cachedAnnotationReader);
  $loggableListener->setUsername('admin');
  $evm->addEventSubscriber($loggableListener);

这对我不起作用,原因有两个:

  1. 我在 services.yml 中注册监听器,而不是在控制器中
  2. 我不希望像示例中那样存储预先知道的用户名,而是存储登录用户的用户名

loggableListener 的方法 setUsername 要么需要一个字符串,要么需要一个带有提供字符串的方法 getUsername 的对象。

我怎样才能将任何一个传递给听众?我找到了一种传递 security_token 的方法,但这还不够。我目前拥有的是这样的:

(...)
gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
    - { name: doctrine.event_subscriber, connection: default }
calls:
    - [ setAnnotationReader, [ "@annotation_reader" ] ]
    #- [ setUsername, [ "A fixed value works" ] ]
    - [ setUserValue, [ @security.??? ] ]

我也在使用 Blameable 并为类似问题找到了一个很好的解决方法(整个监听器被覆盖)。我尝试为 Loggable 做同样的事情,但这似乎有点复杂。

主要问题:如何将安全用户对象(或其用户名)传递给 services.yml 中的侦听器?

更新

Matteo 向我展示了如何将函数的结果作为参数传递给侦听器。这几乎解决了问题。还有另一个服务,它在给定 token_storage 时提供用户名。但这意味着我需要将参数传递给服务,该参数作为参数提供给另一个服务。这个例子将解释:

- [ setUsername, [ "@=service('gedmo.listener.blameable').getUsername( @security.token_storage )" ] ]

现在的问题是,@security.token_storage 在这种情况下不被接受。如何将参数传递给方法 getUsername() ?

4

3 回答 3

2

您可以使用Symfony 服务表达式,例如,您可以尝试以下操作:

gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
    - { name: doctrine.event_subscriber, connection: default }
calls:
    - [ setAnnotationReader, [ "@annotation_reader" ] ]
    - [ setUserValue, ["@=service('security.token_storage').getToken()->getUser()->getUsername()"] ]

但在某些情况下,用户可以是null,因此您可以使用条件?(参见文档)。

希望这有帮助

于 2016-06-08T15:00:03.480 回答
0

我遇到了同样的问题,我通过在stofDoctrineExtensionsBundle中创建一个记录器侦听器来解决它。

为此,我创建了这个文件:

//src/AppBundle/Listener/LoggerListener 
<?php

namespace AppBundle\Listener;

use Gedmo\Loggable\LoggableListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

/**
 * LoggerListener
 *
 * @author Alexandre Tranchant <alexandre.tranchant@gmail.com>
 * @author Christophe Coevoet <stof@notk.org>
 */
class LoggerListener implements EventSubscriberInterface
{
    /**
     * @var AuthorizationCheckerInterface
     */
    private $authorizationChecker;
    /**
     * @var TokenStorageInterface
     */
    private $tokenStorage;
    /**
     * @var LoggableListener
     */
    private $loggableListener;

    /**
     * LoggerListener constructor.
     *
     * @param LoggableListener $loggableListener
     * @param TokenStorageInterface|null $tokenStorage
     * @param AuthorizationCheckerInterface|null $authorizationChecker
     */
    public function __construct(LoggableListener $loggableListener, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authorizationChecker = null)
    {
        $this->loggableListener = $loggableListener;
        $this->tokenStorage = $tokenStorage;
        $this->authorizationChecker = $authorizationChecker;
    }
    /**
     * Set the username from the security context by listening on core.request
     *
     * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
            return;
        }
        if (null === $this->tokenStorage || null === $this->authorizationChecker) {
            return;
        }
        $token = $this->tokenStorage->getToken();
        if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
            $this->loggableListener->setUsername($token);
        }
    }
    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::REQUEST => 'onKernelRequest',
        );
    }
}

然后我在我的services.yml文件中声明了这个监听器。

services:
    #Loggable
    gedmo.listener.loggable:
            class: Gedmo\Loggable\LoggableListener
            tags:
                - { name: doctrine.event_subscriber, connection: default }
            calls:
                - [ setAnnotationReader, [ "@annotation_reader" ] ]

    # KernelRequest listener
    app.listener.loggable:
        class: AppBundle\Listener\LoggerListener
        tags:
            # loggable hooks user username if one is in token_storage or authorization_checker
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest}
        arguments:
            - '@gedmo.listener.loggable'
            - '@security.token_storage'
            - '@security.authorization_checker'

正如您在此转储屏幕截图中看到的那样,它工作正常: 日志转储截图

于 2017-08-06T10:54:21.327 回答
0

我认为不可能将经过身份验证的用户注入服务中。但是您可以注入令牌存储:(@security.token_storage在 symfony 2.6 之前,您必须使用@security.context而不是@security.token_storage)在您的服务(LoggableListener)中,您将能够像这样获得经过身份验证的用户名: $tokenStorage->getToken()->getUser()->getUsername()

于 2016-06-08T14:08:32.173 回答