Symfony中判断请求是否访问受保护页面及监听逻辑实现
在Symfony中创建监听受保护路由的EventListener
这是一个很实用的需求,我来帮你实现一个干净且通用的解决方案,既能准确识别防火墙后方的受保护路由,又能在session中设置标记:
1. 创建EventListener类
首先,我们创建一个事件订阅者(EventSubscriber),它会监听主请求(master request),并判断当前请求是否指向受保护的路由:
// src/EventListener/ProtectedRouteMarkerListener.php namespace App\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Security; use Symfony\Component\HttpFoundation\Session\SessionInterface; class ProtectedRouteMarkerListener implements EventSubscriberInterface { private $security; private $accessDecisionManager; private $session; public function __construct( Security $security, AccessDecisionManagerInterface $accessDecisionManager, SessionInterface $session ) { $this->security = $security; $this->accessDecisionManager = $accessDecisionManager; $this->session = $session; } public static function getSubscribedEvents(): array { return [ // 监听主请求,设置优先级确保在防火墙认证后执行 KernelEvents::REQUEST => ['onMasterRequest', 10], ]; } public function onMasterRequest(RequestEvent $event): void { // 只处理主请求(排除子请求,比如Twig渲染的嵌入式控制器) if (!$event->isMainRequest()) { return; } $request = $event->getRequest(); $currentToken = $this->security->getToken(); // 创建匿名Token,用于测试当前路由是否允许匿名访问 $anonymousToken = new AnonymousToken('app_secret', 'anon.', []); // 检查匿名用户是否有权限访问当前资源 $isPublicRoute = $this->accessDecisionManager->decide( $anonymousToken, ['IS_AUTHENTICATED_ANONYMOUSLY'], $request ); // 如果是受保护路由(匿名用户无法访问) if (!$isPublicRoute) { // 通用标记:记录访问过受保护路由 $this->session->set('visited_protected_route', true); // 可选:针对/admin路由做特殊标记(通过路由名称判断) $routeName = $request->get('_route'); if (str_starts_with($routeName, 'admin_')) { $this->session->set('visited_admin_route', true); } // 可选:仅当用户完全认证时设置标记 if ($this->security->isGranted('IS_AUTHENTICATED_FULLY')) { $this->session->set('authenticated_user_visited_protected', true); } } } }
2. 服务注册(Symfony 4+可跳过)
如果你使用的是Symfony 4及以上版本,只要你的EventListener在src/EventListener目录下,框架会自动注册这个事件订阅者。如果需要手动配置,可在config/services.yaml中添加:
services: App\EventListener\ProtectedRouteMarkerListener: arguments: - '@security.helper' - '@security.access.decision_manager' - '@session' tags: - { name: kernel.event_subscriber }
关键逻辑说明
- 通用判断受保护路由:通过
AccessDecisionManager检查匿名用户是否能访问当前请求,这个方法完全适配你在security.yaml中配置的access_control规则,不需要硬编码路由名称,通用性极强。 - 主请求过滤:通过
$event->isMainRequest()确保我们只处理用户发起的原始请求,排除子请求(比如页面中的嵌入式控制器请求)。 - 灵活的标记设置:你可以根据需求设置不同的标记:
- 所有受保护路由访问记录
- 特定路由(如/admin开头)的访问记录
- 仅当用户完全认证时的访问记录
补充说明
如果你只想针对特定的防火墙区域判断,也可以通过$request->attributes->get('_firewall_context')获取当前请求对应的防火墙上下文名称,再做针对性处理,这种方式适合多防火墙的场景。
内容的提问来源于stack exchange,提问作者Kik Minev
相关产品推荐
相关产品推荐

