Symfony2 注销事件侦听器问题
Symfony2 Logout Event Listener Issue
我正在努力将审计日志记录添加到 Symfony2 项目,该项目将所有页面加载和 post 请求记录到自定义审计 table 中。该项目使用 Symfony2 的默认注销路由(访问 /logout),它会破坏会话,然后重新定向到 /login 路由。
为 onKernelRequest 设置了一个事件侦听器,然后将正确的数据写入 table。在 security.yml 文件中,我为注销路由列出了以下内容。
security:
firewalls:
main:
logout:
path: /logout
target: /login
除注销事件外,所有页面的审核日志记录工作正常。注销后,我尝试访问探查器,然后从侧边栏的 "Last 10" 选项中选择“/logout”操作。单击 "Events" 时,会列出 kernel.request 的默认 Symfony 事件,例如 DebugHandler 和 ProfileListener,但是我的自定义回调显示在 "Not Called Listeners" 选项卡下。
有一个 success_handler 可以添加到 security.yml 文件,但是我需要一种方法可以 运行 我的事件侦听器,然后会话被销毁。有没有办法让现有的侦听器在 Symfony 执行注销操作之前也记录注销事件?
编辑
<?php
// src/AuditBundle/EventListener/AuditListener.php
namespace AuditBundle\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use AuditBundle\Entity\AuditLog;
class AuditListener
{
protected $requestStack;
protected $em;
protected $tokenStorage;
protected $authorizationChecker;
public function __construct(RequestStack $requestStack, \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage $tokenStorage, $authorizationChecker, \Doctrine\ORM\EntityManager $em = NULL)
{
$this->requestStack = $requestStack;
$this->em = $em;
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
}
public function onKernelRequest(GetResponseEvent $response)
{
$request = $response->getRequest();
if ( strpos($request->getRequestUri(), 'fonts') !== false)
return;
if ( strpos($request->getRequestUri(), 'css') !== false)
return;
if ( strpos($request->getRequestUri(), '_wdt') !== false)
return;
if ( strpos($request->getRequestUri(), 'js') !== false)
return;
if ( strpos($request->getRequestUri(), '_profiler') !== false)
return;
$this->log('Request', $request);
}
public function postUpdate(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Updated', $this->requestStack->getCurrentRequest(), $entity);
}
public function postPersist(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Created', $this->requestStack->getCurrentRequest(), $entity);
}
public function postDelete(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Deleted', $this->requestStack->getCurrentRequest());
}
protected function log($message, $request, $entity = NULL)
{
$log = new AuditLog();
$log->setType($request->getRealMethod());
if ($this->authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY'))
{
$log->setUser($this->tokenStorage->getToken()->getUser());
}
if ($entity)
{
$log->setEntityId($entity->getId());
}
$log->setUriString($request->getRequestUri());
$log->setMessage($message);
$log->setDatetime(new \DateTime());
$log->setIp($request->getClientIp());
$log->setBrowser(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
$this->em->persist($log);
$this->em->flush();
}
}
services.yml
services:
audits.audit_listener:
class: AuditBundle\EventListener\AuditListener
arguments: [@request_stack, @security.token_storage, @security.authorization_checker, @doctrine.orm.entity_manager]
tags:
- { name: kernel.event_listener, event: kernel.request }
由于我不清楚你们的 Audit logging
服务,我猜,注销事件发生在手边。因此,您应该尝试通过实现另一个实现 LogoutHandlerInterface
并且依赖于您的 Audit logging
服务的 LogoutHandler
服务来调用您的 Audit logging
服务。
在我们看到日志中发生了什么之后,我们可以得到一个清晰的思路。
挂接到 Symfony 的注销事件的最佳方法是像这样实现 LogoutHandlerInterface,
事件侦听器:
<?php
namespace AppBundle\EventListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
class MyListener implements LogoutHandlerInterface
{
public function logout(Request $request, Response $response, TokenInterface $token)
{
// Your handling here
}
}
配置:
services:
appbundle_mylistener:
class: AppBundle\EventListener\MyListener
security:
firewalls:
main:
logout:
handlers: [appbundle_mylistener]
因此,您真正需要做的就是使用 logout
方法实现 AuditListener
中的 LogoutHandlerInterface
,然后在配置中注册 handlers
参数
我正在努力将审计日志记录添加到 Symfony2 项目,该项目将所有页面加载和 post 请求记录到自定义审计 table 中。该项目使用 Symfony2 的默认注销路由(访问 /logout),它会破坏会话,然后重新定向到 /login 路由。
为 onKernelRequest 设置了一个事件侦听器,然后将正确的数据写入 table。在 security.yml 文件中,我为注销路由列出了以下内容。
security:
firewalls:
main:
logout:
path: /logout
target: /login
除注销事件外,所有页面的审核日志记录工作正常。注销后,我尝试访问探查器,然后从侧边栏的 "Last 10" 选项中选择“/logout”操作。单击 "Events" 时,会列出 kernel.request 的默认 Symfony 事件,例如 DebugHandler 和 ProfileListener,但是我的自定义回调显示在 "Not Called Listeners" 选项卡下。
有一个 success_handler 可以添加到 security.yml 文件,但是我需要一种方法可以 运行 我的事件侦听器,然后会话被销毁。有没有办法让现有的侦听器在 Symfony 执行注销操作之前也记录注销事件?
编辑
<?php
// src/AuditBundle/EventListener/AuditListener.php
namespace AuditBundle\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use AuditBundle\Entity\AuditLog;
class AuditListener
{
protected $requestStack;
protected $em;
protected $tokenStorage;
protected $authorizationChecker;
public function __construct(RequestStack $requestStack, \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage $tokenStorage, $authorizationChecker, \Doctrine\ORM\EntityManager $em = NULL)
{
$this->requestStack = $requestStack;
$this->em = $em;
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
}
public function onKernelRequest(GetResponseEvent $response)
{
$request = $response->getRequest();
if ( strpos($request->getRequestUri(), 'fonts') !== false)
return;
if ( strpos($request->getRequestUri(), 'css') !== false)
return;
if ( strpos($request->getRequestUri(), '_wdt') !== false)
return;
if ( strpos($request->getRequestUri(), 'js') !== false)
return;
if ( strpos($request->getRequestUri(), '_profiler') !== false)
return;
$this->log('Request', $request);
}
public function postUpdate(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Updated', $this->requestStack->getCurrentRequest(), $entity);
}
public function postPersist(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Created', $this->requestStack->getCurrentRequest(), $entity);
}
public function postDelete(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Deleted', $this->requestStack->getCurrentRequest());
}
protected function log($message, $request, $entity = NULL)
{
$log = new AuditLog();
$log->setType($request->getRealMethod());
if ($this->authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY'))
{
$log->setUser($this->tokenStorage->getToken()->getUser());
}
if ($entity)
{
$log->setEntityId($entity->getId());
}
$log->setUriString($request->getRequestUri());
$log->setMessage($message);
$log->setDatetime(new \DateTime());
$log->setIp($request->getClientIp());
$log->setBrowser(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
$this->em->persist($log);
$this->em->flush();
}
}
services.yml
services:
audits.audit_listener:
class: AuditBundle\EventListener\AuditListener
arguments: [@request_stack, @security.token_storage, @security.authorization_checker, @doctrine.orm.entity_manager]
tags:
- { name: kernel.event_listener, event: kernel.request }
由于我不清楚你们的 Audit logging
服务,我猜,注销事件发生在手边。因此,您应该尝试通过实现另一个实现 LogoutHandlerInterface
并且依赖于您的 Audit logging
服务的 LogoutHandler
服务来调用您的 Audit logging
服务。
在我们看到日志中发生了什么之后,我们可以得到一个清晰的思路。
挂接到 Symfony 的注销事件的最佳方法是像这样实现 LogoutHandlerInterface,
事件侦听器:
<?php
namespace AppBundle\EventListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
class MyListener implements LogoutHandlerInterface
{
public function logout(Request $request, Response $response, TokenInterface $token)
{
// Your handling here
}
}
配置:
services:
appbundle_mylistener:
class: AppBundle\EventListener\MyListener
security:
firewalls:
main:
logout:
handlers: [appbundle_mylistener]
因此,您真正需要做的就是使用 logout
方法实现 AuditListener
中的 LogoutHandlerInterface
,然后在配置中注册 handlers
参数