Передача имени защищенного пользователя слушателю в services.yml в Symfony2

Я использую Gedmo Loggable, чтобы отслеживать изменения, которые пользователи вносят в объекты. Имя пользователя не сохраняется по умолчанию для каждого изменения, но его необходимо предоставить слушателю.

Пример этого можно найти в документации Loggable:

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

Это не работает для меня по двум причинам:

  1. Я регистрирую слушателя в services.yml, а не в контроллере
  2. Я не хочу хранить заранее известное имя пользователя, как в примере, но имя пользователя, который вошел в систему

Метод setUsername объекта loggableListener ожидает строку или объект с методом 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?

Обновить

Маттео показал мне, как передать результат функции в качестве параметра слушателю. Это почти решает проблему. Существует еще один сервис, который предоставляет имя пользователя при получении token_storage. Но это означает, что мне нужно передать параметр службе, которая передается в качестве параметра другой службе. Этот пример объяснит:

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

Проблема в том, что @ security.token_storage не принимается в этом контексте. Как я могу передать параметр в метод getUsername()?

3 ответа

Вы можете использовать выражения службы 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, так что вы можете условие с ? (см. документ).

Надеюсь это поможет

Я думаю, что невозможно ввести авторизованного пользователя в сервис. Но вы можете ввести токен в хранилище: @security.token_storage (до Symfony 2.6 вам придется использовать @security.context вместо @security.token_storage) В вашем сервисе (LoggableListener) вы сможете получить аутентифицированное имя пользователя следующим образом:$tokenStorage->getToken()->getUser()->getUsername()

Я столкнулся с той же проблемой и решил ее, создав прослушиватель журнала, как в https://github.com/stof/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'

Как вы можете видеть на этом снимке экрана, он работает нормально: Скриншот лог-дампа

Другие вопросы по тегам