From b595dc1dfad9db534ca7b9e8f46bb9926b88ab5a Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 22 Apr 2021 18:29:07 +0100 Subject: [PATCH] Add advice on Spring MVC path matching for 5.3+ Closes gh-26750 --- src/docs/asciidoc/web/webmvc.adoc | 52 ++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index f058f3319b96..27b825c43d09 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -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 <> 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 +<>. 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 @@ -1650,7 +1700,7 @@ configuration. [.small]#<># 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)`]