Symfony EventSubscriber如何检测需身份验证路由并触发404页面
实现方案
以下针对Symfony框架场景实现,全程不需要修改原有控制器的鉴权注解,仅通过EventSubscriber完成检测和跳转逻辑。
前置配置
首先在.env文件中添加开关配置:
# 开启后所有要求IS_AUTHENTICATED_FULLY的路由直接返回404 AUTH_REDIRECT_TO_404=true
在config/services.yaml中绑定该参数为服务参数:
parameters: app.auth_redirect_to_404: '%env(bool:AUTH_REDIRECT_TO_404)%'
编写EventSubscriber
<?php namespace App\EventSubscriber; use Doctrine\Common\Annotations\Reader; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Annotation\IsGranted; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; class AuthRouteHideSubscriber implements EventSubscriberInterface { public function __construct( private Reader $annotationReader, private ParameterBagInterface $parameterBag ){} public static function getSubscribedEvents(): array { // 优先级高于安全组件的鉴权监听,提前拦截 return [ KernelEvents::REQUEST => ['onKernelRequest', 10], ]; } public function onKernelRequest(RequestEvent $event): void { // 仅处理主请求 if (!$event->isMainRequest()) { return; } // 开关未开启直接跳过 if (!$this->parameterBag->get('app.auth_redirect_to_404')) { return; } $request = $event->getRequest(); $controller = $request->attributes->get('_controller'); // 解析控制器类和方法名 if (is_string($controller) && str_contains($controller, '::')) { [$controllerClass, $methodName] = explode('::', $controller, 2); } elseif (is_array($controller)) { [$controllerClass, $methodName] = $controller; } else { return; } $needAuth = false; $reflectionClass = new \ReflectionClass($controllerClass); // 检测类级别@IsGranted注解 /** @var IsGranted[] $classGrants */ $classGrants = $this->annotationReader->getClassAnnotations($reflectionClass, IsGranted::class); foreach ($classGrants as $grant) { if ($grant->attributes === 'IS_AUTHENTICATED_FULLY') { $needAuth = true; break; } } // 类没有配置的话检测方法级别注解 if (!$needAuth && $reflectionClass->hasMethod($methodName)) { $reflectionMethod = $reflectionClass->getMethod($methodName); /** @var IsGranted[] $methodGrants */ $methodGrants = $this->annotationReader->getMethodAnnotations($reflectionMethod, IsGranted::class); foreach ($methodGrants as $grant) { if ($grant->attributes === 'IS_AUTHENTICATED_FULLY') { $needAuth = true; break; } } } // 符合条件返回404 if ($needAuth) { throw new NotFoundHttpException(); } } }
注意事项
- 如果你使用的是Symfony 6+的PHP属性而非注解,把
Doctrine\Common\Annotations\Reader替换为Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface,对应修改读取属性的逻辑即可 - 如果你在
security.yaml中配置了access_control规则,需要额外注入Symfony\Component\Security\Http\AccessMapInterface,匹配当前请求的路径是否属于要求IS_AUTHENTICATED_FULLY的规则 - 上述代码抛出
NotFoundHttpException后,Symfony会自动渲染项目配置的404页面,和真实不存在的路由返回效果完全一致
内容的提问来源于stack exchange,提问作者spacecodeur
相关产品推荐
相关产品推荐

