You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:12:24