Skip to content

路径匹配 (Path Matching)

Servlet API 将完整的请求路径暴露为 requestURI,并进一步将其细分为 contextPathservletPathpathInfo。这些值取决于 Servlet 的映射方式。从这些输入中,Spring MVC 需要确定用于映射处理器的查找路径(Lookup Path),该路径应排除 contextPath 和任何适用的 servletMapping 前缀。

路径解析的挑战

servletPathpathInfo 是经过解码的,这使得它们无法直接与完整的 requestURI 进行比较以派生查找路径,因此必须对 requestURI 进行解码。然而,这会引入新的问题,因为路径可能包含编码后的保留字符(如 "/"";"),解码后会改变路径结构,甚至导致安全问题。此外,不同的 Servlet 容器对 servletPath 的规范化程度不同,这使得基于 startsWithrequestURI 比较变得几乎不可能。

因此,最好避免依赖 servletPath。如果 DispatcherServlet 被映射为默认 Servlet(使用 "/")或不带前缀(使用 "/*"),且 Servlet 容器版本为 4.0+,则 Spring MVC 能够检测到映射类型,从而完全避免使用 servletPathpathInfo

PathPatternParser (推荐)

从 Spring Framework 5.3 开始引入了 PathPatternParser,并在 6.0 中默认启用。与需要解码查找路径或编码控制器映射的 AntPathMatcher 不同,parsed PathPattern 会逐个段(Segment)地匹配经过解析的路径表示(称为 RequestPath)。

为什么 PathPatternParser 更好?

  • 安全性:可以单独对路径段进行解码和清理,而不会改变整体路径结构。
  • 性能:解析后的模式比普通的字符串匹配更高效。
  • 语义清晰:支持简单的子路径匹配,且对 URL 中的特殊字符处理更鲁棒。

补充教学

1. 传统 AntPathMatcher vs 现代 PathPatternParser

  • AntPathMatcher (旧):基于字符串的简单匹配(如 /**/*.html)。它的缺点是无法处理某些复杂的 URL 编码场景,且在处理大批量映射时性能会线性下降。
  • PathPatternParser (新):它将 URL 和模式都预先解析为树状结构。在匹配时,它是按“层级”跳转的,速度极快,且原生支持更加安全的路径段处理。

2. 为什么映射 "/" 比 "/*" 更好?

  • /*:这是一个前缀映射。Servlet 容器会认为这个 Servlet 负责处理所有请求。这会导致 servletPath 包含前缀,增加路径解析的复杂度。
  • /:这是“默认 Servlet”映射。当容器找不到更具体的映射时,才会交给这个 Servlet。这是 Spring MVC 的推荐映射方式,因为它让 Spring 有更大的余地来计算相对于根的查找路径(Lookup Path)。

3. 安全警告:/;/ 攻击

在某些旧版本的 Java Web 应用中,攻击者可以通过在 URL 中插入分号(如 /admin;/login)来绕过安全过滤器。 Spring 的应对:现代的 PathPatternParser 和 Spring Security 的防火墙会严格检查并解析这些字符。使用 PathPatternParser 能显著降低由于 URL 编码不当导致的权限绕过风险。

Based on Spring Framework.