Symfony 5+如何批量设置大量路由为公开(无需认证)
问题
项目中有大量路由,部分为公开路由,部分需要认证。当前使用自定义RequestMatcher批量设置公开路由,但存在两个问题:一是路由列表需要和控制器分开维护,容易出现不一致;二是处理尾随斜杠时存在匹配问题。有没有更优雅的批量设置公开路由的方式?
当前实现代码:
自定义RequestMatcher
final class RequestMatcher implements RequestMatcherInterface { private const PUBLIC_ROUTES = [ '/img/.*', '/v1/page/loading.*', '/v1/page2/[^/]+/success.*', //...15 more routes ]; public function matches(Request $request): bool { $pattern = '~^(?:' . implode('|', self::PUBLIC_ROUTES) . ')$~'; if (preg_match($pattern, $request->getPathInfo())) { return true; } return false; } }
security.yml配置
security: firewalls: public: request_matcher: App\Security\RequestMatcher security: false api: pattern: ^/ stateless: true provider: hmac guard: authenticators: - my_hmac_authenticator
优化方案
方案1:通过路由属性标记公开路由
在定义路由时给公开路由添加自定义属性(比如_is_public: true),然后通过路由匹配器直接读取该属性,彻底避免维护两套路由列表。
步骤1:给公开路由添加属性
如果用注解定义路由:
use Symfony\Component\Routing\Annotation\Route; class PublicController { /** * @Route("/v1/page/loading", name="page_loading", defaults={"_is_public": true}) */ public function loading() { // ... } /** * @Route("/img/{path}", name="asset_img", defaults={"_is_public": true, "path": ""}) */ public function serveImage(string $path) { // ... } }
如果用yaml定义路由:
# config/routes.yaml page_loading: path: /v1/page/loading controller: App\Controller\PublicController::loading defaults: _is_public: true asset_img: path: /img/{path} controller: App\Controller\PublicController::serveImage defaults: _is_public: true path: ''
步骤2:修改RequestMatcher读取路由属性
注入RouterInterface,通过请求路径匹配到路由后,判断是否存在_is_public属性:
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Http\Firewall\RequestMatcherInterface; final class PublicRouteRequestMatcher implements RequestMatcherInterface { public function __construct(private RouterInterface $router) { } public function matches(Request $request): bool { try { $route = $this->router->match($request->getPathInfo()); return isset($route['_is_public']) && $route['_is_public'] === true; } catch (\Exception) { // 匹配不到路由的情况默认不公开 return false; } } }
步骤3:更新security.yml配置
替换原来的request_matcher为这个新类即可,配置逻辑和之前保持一致。
该方案优势:路由规则仅在一处维护,完全和控制器路由同步,且Symfony路由系统会自动处理/path和/path/的尾随斜杠匹配问题。
方案2:利用路由前缀批量匹配
如果公开路由有统一的前缀(比如/img/、/v1/public/),可以直接在security.yml里用pattern配置,无需编写自定义类:
security: firewalls: public: # 直接匹配公开路由前缀,/?允许结尾有或没有斜杠 pattern: ^/(img|v1/page/loading|v1/page2/[^/]+/success)/?$ security: false api: pattern: ^/ stateless: true provider: hmac guard: authenticators: - my_hmac_authenticator
如果前缀更统一(比如所有公开路由都在/public/下),可简化为pattern: ^/public/,配置更简洁。
方案3:改进现有正则处理尾随斜杠
若不想改动现有结构,仅需优化正则表达式,允许路径结尾有可选的斜杠:
public function matches(Request $request): bool { // 给每个路由规则添加/?,兼容有无尾随斜杠的情况 $publicRoutes = array_map(fn($route) => $route . '/?', self::PUBLIC_ROUTES); $pattern = '~^(?:' . implode('|', $publicRoutes) . ')$~'; return preg_match($pattern, $request->getPathInfo()) === 1; }
但此方案仍存在维护两套路由的问题,仅解决了尾随斜杠匹配问题。
内容的提问来源于stack exchange,提问作者Lucas Borges
相关产品推荐
相关产品推荐

