Symfony中DataDog/AuditBundle执行顺序问题致无法获取用户Token
解决Symfony中AuditBundle事件订阅者早于TokenAuthenticator执行导致Token为null的问题
这个问题我之前也碰到过,本质是Symfony事件订阅的优先级顺序导致的——AuditBundle的订阅者在TokenAuthenticator完成认证前就触发了,所以TokenStorageInterface里还没有用户Token。下面给你几个可行的解决方案,按改动成本从小到大排序:
1. 调整Audit订阅者的事件优先级
Symfony事件订阅者的默认优先级是0,数值越大越先执行。我们可以把AuditBundle的订阅者优先级设为负数,让它在TokenAuthenticator完成认证后再触发。
情况A:自定义的Audit订阅者
如果是你自己写的订阅者,直接在getSubscribedEvents方法里指定更低的优先级:
use Symfony\Component\HttpKernel\KernelEvents; class CustomAuditSubscriber implements EventSubscriberInterface { private $securityTokenStorage; public function __construct(TokenStorageInterface $securityTokenStorage) { $this->securityTokenStorage = $securityTokenStorage; } public static function getSubscribedEvents(): array { return [ // 把优先级设为-100,确保在认证逻辑之后执行 KernelEvents::REQUEST => [['onKernelRequest', -100]], ]; } public function onKernelRequest(RequestEvent $event) { $token = $this->securityTokenStorage->getToken(); // 这里就能拿到已认证的用户Token了 if ($token && $token->getUser()) { $user = $token->getUser(); // 执行审计日志逻辑 } } }
情况B:AuditBundle自带的订阅者
如果是DataDog/AuditBundle自带的订阅者,你可以在services.yaml里重新定义该服务,覆盖它的事件优先级:
services: Datadog\AuditBundle\EventSubscriber\AuditSubscriber: arguments: $securityTokenStorage: '@security.token_storage' # 保留Bundle原有的其他参数 $auditConfig: '@Datadog\AuditBundle\Configuration\AuditConfig' $auditLogger: '@Datadog\AuditBundle\Logger\AuditLogger' tags: # 设置更低的优先级,让它晚于认证逻辑执行 - { name: kernel.event_subscriber, priority: -100 }
2. 延迟获取Token到认证完成后的事件
如果调整优先级无效,你可以让Audit逻辑监听更晚的事件,比如kernel.controller——这个事件是在路由匹配、认证完成后才触发的,此时Token肯定已经存在。
修改订阅者的事件监听:
public static function getSubscribedEvents(): array { return [ KernelEvents::CONTROLLER => ['onKernelController'], ]; } public function onKernelController(ControllerEvent $event) { $token = $this->securityTokenStorage->getToken(); if ($token && $token->getUser()) { $user = $token->getUser(); // 执行审计日志逻辑 } }
3. 在TokenAuthenticator认证成功后触发自定义事件
如果你的API认证逻辑是自定义的TokenAuthenticator,可以在认证成功后手动触发一个自定义事件,让Audit订阅者监听这个事件,确保只在认证完成后处理日志。
步骤1:在TokenAuthenticator中触发自定义事件
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; class TokenAuthenticator extends AbstractGuardAuthenticator { private $eventDispatcher; public function __construct(EventDispatcherInterface $eventDispatcher) { $this->eventDispatcher = $eventDispatcher; } public function authenticate(Request $request): PassportInterface { // 你的Token认证逻辑... $user = $this->userProvider->loadUserByIdentifier($username); $token = new UsernamePasswordToken($user, null, 'api', $user->getRoles()); // 认证成功后触发自定义事件 $this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($token), 'api.authentication.success'); return new SelfValidatingPassport(new UserBadge($username)); } }
步骤2:让Audit订阅者监听自定义事件
public static function getSubscribedEvents(): array { return [ 'api.authentication.success' => ['onAuthenticationSuccess'], ]; } public function onAuthenticationSuccess(AuthenticationSuccessEvent $event) { $token = $event->getToken(); $user = $token->getUser(); // 直接拿到已认证用户,执行审计日志逻辑 }
注意事项
不管用哪种方案,都建议在获取Token后加一层判断,避免未认证请求导致的报错:
$token = $this->securityTokenStorage->getToken(); if (!$token instanceof TokenInterface || !$token->getUser()) { // 处理未认证情况,比如跳过日志或记录匿名用户 return; }
内容的提问来源于stack exchange,提问作者Vipulw
相关产品推荐
相关产品推荐

