Skip to content

Commit

Permalink
Add advice on Spring MVC path matching for 5.3+
Browse files Browse the repository at this point in the history
Closes gh-26750
  • Loading branch information
rstoyanchev committed Apr 26, 2021
1 parent 86123de commit b595dc1
Showing 1 changed file with 51 additions and 1 deletion.
52 changes: 51 additions & 1 deletion src/docs/asciidoc/web/webmvc.adoc
Expand Up @@ -573,6 +573,56 @@ initialization parameters (`init-param` elements) to the Servlet declaration in



[[mvc-handlermapping-path]]
=== Path Matching

The Servlet API exposes the full request path as `requestURI` and further sub-divides it
into `contextPath`, `servletPath`, and `pathInfo` whose values vary depending on how a
Servlet is mapped. From these inputs, Spring MVC needs to determine the lookup path to
use for handler mapping, which is the path within the mapping of the `DispatcherServlet`
itself, excluding the `contextPath` and any `servletMapping` prefix, if present.

The `servletPath` and `pathInfo` are decoded and that makes them impossible to compare
directly to the full `requestURI` in order to derive the lookupPath and that makes it
necessary to decode the `requestUri`. However this introduces its own issues because the
path may contain encoded reserved characters such as `"/"` or `";"` that can in turn
alter the structure of the path after they are decoded which can also lead to security
issues. In addition, Servlet containers may normalize the `servletPath` to varying
degrees which makes it further impossible to perform `startsWith` comparisons against
the `requestUri`.

This is why it is best to avoid reliance on the `servletPath` which comes with the
prefix-based `servletPath` mapping type. If the `DispatcherServlet` is mapped as the
default Servlet with `"/"` or otherwise without a prefix with `"/*"` and the Servlet
container is 4.0+ then Spring MVC is able to detect the Servlet mapping type and avoid
use of the `servletPath` and `pathInfo` altogether. On a 3.1 Servlet container,
assuming the same Servlet mapping types, the equivalent can be achieved by providing
a `UrlPathHelper` with `alwaysUseFullPath=true` via <<mvc-config-path-matching>> in
the MVC config.

Fortunately the default Servlet mapping `"/"` is a good choice. However, there is still
an issue in that the `requestUri` needs to be decoded to make it possible to compare to
controller mappings. This is again undesirable because of the potential to decode
reserved characters that alter the path structure. If such characters are not expected,
then you can reject them (like the Spring Security HTTP firewall), or you can configure
`UrlPathHelper` with `urlDecode=false` but controller mappings will need to match to the
encoded path which may not always work well. Furthermore, sometimes the
`DispatcherServlet` needs to share the URL space with another Servlet and may need to
be mapped by prefix.

The above issues can be addressed more comprehensively by switching from `PathMatcher` to
the parsed `PathPattern` available in 5.3 or higher, see
<<mvc-ann-requestmapping-pattern-comparison>>. Unlike `AntPathMatcher` which needs
either the lookup path decoded or the controller mapping encoded, a parsed `PathPattern`
matches to a parsed representation of the path called `RequestPath`, one path segment
at a time. This allows decoding and sanitizing path segment values individually without
the risk of altering the structure of the path. Parsed `PathPattern` also supports
the use of `servletPath` prefix mapping as long as the prefix is kept simple and does
not have any characters that need to be encoded.




[[mvc-handlermapping-interceptor]]
=== Interception

Expand Down Expand Up @@ -1650,7 +1700,7 @@ configuration.
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-pattern-comparison, WebFlux>>#

When multiple patterns match a URL, the best match must be selected. This is done with
one of the following depending on whether parsed `PathPattern`'s are enabled for use or not:
one of the following depending on whether use of parsed `PathPattern` is enabled for use or not:

* {api-spring-framework}/web/util/pattern/PathPattern.html#SPECIFICITY_COMPARATOR[`PathPattern.SPECIFICITY_COMPARATOR`]
* {api-spring-framework}/util/AntPathMatcher.html#getPatternComparator-java.lang.String-[`AntPathMatcher.getPatternComparator(String path)`]
Expand Down

0 comments on commit b595dc1

Please sign in to comment.