From 4d525909642bc25e2addd8b2c4980a7ac26eee74 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 1 Jun 2017 22:36:12 -0400 Subject: [PATCH] Improve docs on forwarded headers Issue: SPR-15612 --- .../web/util/UriComponentsBuilder.java | 2 +- .../annotation/MvcUriComponentsBuilder.java | 67 ++++++++++++++++--- .../support/ServletUriComponentsBuilder.java | 37 +++++++++- src/docs/asciidoc/web/web-mvc.adoc | 32 +++++++++ 4 files changed, 126 insertions(+), 12 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 9cbb84be575..7ce308ec9bb 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -275,7 +275,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable { /** * Create a new {@code UriComponents} object from the URI associated with * the given HttpRequest while also overlaying with values from the headers - * "Forwarded" (RFC 7239, + * "Forwarded" (RFC 7239), * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if * "Forwarded" is not found. * @param request the source request diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index fe343220d3a..9520b5bbee1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -70,18 +70,28 @@ import org.springframework.web.util.UriComponentsBuilder; /** * Creates instances of {@link org.springframework.web.util.UriComponentsBuilder} - * by pointing to Spring MVC controllers and {@code @RequestMapping} methods. + * by pointing to {@code @RequestMapping} methods on Spring MVC controllers. * - *

The static {@code fromXxx(...)} methods prepare links relative to the - * current request as determined by a call to + *

There are several groups of methods: + *

* - *

The static {@code fromXxx(UriComponentsBuilder,...)} methods can be given - * the baseUrl when operating outside the context of a request. - * - *

You can also create an MvcUriComponentsBuilder instance with a baseUrl - * via {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)} - * and then use the non-static {@code withXxx(...)} method variants. + *

Note: This class extracts and uses values from the headers + * "Forwarded" (RFC 7239), + * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if + * "Forwarded" is not found, in order to reflect the client-originated protocol + * and address. As an alternative consider using the + * {@link org.springframework.web.filter.ForwardedHeaderFilter} to have such + * headers extracted once and removed, or removed only (without being used). + * See the reference for further information including security considerations. * * @author Oliver Gierke * @author Rossen Stoyanchev @@ -142,6 +152,8 @@ public class MvcUriComponentsBuilder { * Create a {@link UriComponentsBuilder} from the mapping of a controller class * and current request information including Servlet mapping. If the controller * contains multiple mappings, only the first one is used. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param controllerType the controller to build a URI for * @return a UriComponentsBuilder instance (never {@code null}) */ @@ -154,6 +166,8 @@ public class MvcUriComponentsBuilder { * {@code UriComponentsBuilder} representing the base URL. This is useful * when using MvcUriComponentsBuilder outside the context of processing a * request or to apply a custom baseUrl not matching the current request. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param builder the builder for the base URL; the builder will be cloned * and therefore not modified and may be re-used for further calls. * @param controllerType the controller to build a URI for @@ -171,6 +185,8 @@ public class MvcUriComponentsBuilder { * Create a {@link UriComponentsBuilder} from the mapping of a controller * method and an array of method argument values. This method delegates * to {@link #fromMethod(Class, Method, Object...)}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param controllerType the controller * @param methodName the method name * @param args the argument values @@ -190,6 +206,8 @@ public class MvcUriComponentsBuilder { * accepts a {@code UriComponentsBuilder} representing the base URL. This is * useful when using MvcUriComponentsBuilder outside the context of processing * a request or to apply a custom baseUrl not matching the current request. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param builder the builder for the base URL; the builder will be cloned * and therefore not modified and may be re-used for further calls. * @param controllerType the controller @@ -237,6 +255,10 @@ public class MvcUriComponentsBuilder { * controller.getAddressesForCountry("US") * builder = MvcUriComponentsBuilder.fromMethodCall(controller); * + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. + * * @param info either the value returned from a "mock" controller * invocation or the "mock" controller itself after an invocation * @return a UriComponents instance @@ -255,6 +277,8 @@ public class MvcUriComponentsBuilder { * {@code UriComponentsBuilder} representing the base URL. This is useful * when using MvcUriComponentsBuilder outside the context of processing a * request or to apply a custom baseUrl not matching the current request. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param builder the builder for the base URL; the builder will be cloned * and therefore not modified and may be re-used for further calls. * @param info either the value returned from a "mock" controller @@ -305,6 +329,10 @@ public class MvcUriComponentsBuilder { * *

Note that it's not necessary to specify all arguments. Only the ones * required to prepare the URL, mainly {@code @RequestParam} and {@code @PathVariable}). + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. + * * @param mappingName the mapping name * @return a builder to prepare the URI String * @throws IllegalArgumentException if the mapping name is not found or @@ -320,6 +348,8 @@ public class MvcUriComponentsBuilder { * {@code UriComponentsBuilder} representing the base URL. This is useful * when using MvcUriComponentsBuilder outside the context of processing a * request or to apply a custom baseUrl not matching the current request. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param builder the builder for the base URL; the builder will be cloned * and therefore not modified and may be re-used for further calls. * @param name the mapping name @@ -352,6 +382,8 @@ public class MvcUriComponentsBuilder { * {@link org.springframework.web.method.support.UriComponentsContributor * UriComponentsContributor}) while remaining argument values are ignored and * can be {@code null}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param controllerType the controller type * @param method the controller method * @param args argument values for the controller method @@ -368,6 +400,8 @@ public class MvcUriComponentsBuilder { * This is useful when using MvcUriComponentsBuilder outside the context of * processing a request or to apply a custom baseUrl not matching the * current request. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param baseUrl the builder for the base URL; the builder will be cloned * and therefore not modified and may be re-used for further calls. * @param controllerType the controller type @@ -549,6 +583,9 @@ public class MvcUriComponentsBuilder { *

 	 * MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build();
 	 * 
+ *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. + * * @param controllerType the target controller */ public static T on(Class controllerType) { @@ -571,6 +608,8 @@ public class MvcUriComponentsBuilder { * fooController.saveFoo(2, null); * builder = MvcUriComponentsBuilder.fromMethodCall(fooController); * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @param controllerType the target controller */ public static T controller(Class controllerType) { @@ -626,6 +665,8 @@ public class MvcUriComponentsBuilder { /** * An alternative to {@link #fromController(Class)} for use with an instance * of this class created via a call to {@link #relativeTo}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @since 4.2 */ public UriComponentsBuilder withController(Class controllerType) { @@ -635,6 +676,8 @@ public class MvcUriComponentsBuilder { /** * An alternative to {@link #fromMethodName(Class, String, Object...)}} for * use with an instance of this class created via {@link #relativeTo}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @since 4.2 */ public UriComponentsBuilder withMethodName(Class controllerType, String methodName, Object... args) { @@ -644,6 +687,8 @@ public class MvcUriComponentsBuilder { /** * An alternative to {@link #fromMethodCall(Object)} for use with an instance * of this class created via {@link #relativeTo}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @since 4.2 */ public UriComponentsBuilder withMethodCall(Object invocationInfo) { @@ -653,6 +698,8 @@ public class MvcUriComponentsBuilder { /** * An alternative to {@link #fromMappingName(String)} for use with an instance * of this class created via {@link #relativeTo}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @since 4.2 */ public MethodArgumentBuilder withMappingName(String mappingName) { @@ -662,6 +709,8 @@ public class MvcUriComponentsBuilder { /** * An alternative to {@link #fromMethod(Class, Method, Object...)} * for use with an instance of this class created via {@link #relativeTo}. + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. * @since 4.2 */ public UriComponentsBuilder withMethod(Class controllerType, Method method, Object... args) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java index 8cc802ba4f4..e8b7a34f535 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java @@ -17,7 +17,6 @@ package org.springframework.web.servlet.support; import java.util.Enumeration; - import javax.servlet.http.HttpServletRequest; import org.springframework.http.HttpRequest; @@ -34,7 +33,17 @@ import org.springframework.web.util.UriUtils; import org.springframework.web.util.UrlPathHelper; /** - * A UriComponentsBuilder that extracts information from the HttpServletRequest. + * UriComponentsBuilder with additional static factory methods to create links + * based on the current HttpServletRequest. + * + *

Note: This class extracts and uses values from the headers + * "Forwarded" (RFC 7239), + * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if + * "Forwarded" is not found, in order to reflect the client-originated protocol + * and address. As an alternative consider using the + * {@link org.springframework.web.filter.ForwardedHeaderFilter} to have such + * headers extracted once and removed, or removed only (without being used). + * See the reference for further information including security considerations. * * @author Rossen Stoyanchev * @since 3.1 @@ -71,6 +80,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Prepare a builder from the host, port, scheme, and context path of the * given HttpServletRequest. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest request) { ServletUriComponentsBuilder builder = initFromRequest(request); @@ -85,6 +97,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { * will end with "/main". If the servlet is mapped otherwise, e.g. * {@code "/"} or {@code "*.do"}, the result will be the same as * if calling {@link #fromContextPath(HttpServletRequest)}. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest request) { ServletUriComponentsBuilder builder = fromContextPath(request); @@ -97,6 +112,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Prepare a builder from the host, port, scheme, and path (but not the query) * of the HttpServletRequest. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest request) { ServletUriComponentsBuilder builder = initFromRequest(request); @@ -107,6 +125,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Prepare a builder by copying the scheme, host, port, path, and * query string of an HttpServletRequest. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromRequest(HttpServletRequest request) { ServletUriComponentsBuilder builder = initFromRequest(request); @@ -155,6 +176,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Same as {@link #fromContextPath(HttpServletRequest)} except the * request is obtained through {@link RequestContextHolder}. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromCurrentContextPath() { return fromContextPath(getCurrentRequest()); @@ -163,6 +187,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Same as {@link #fromServletMapping(HttpServletRequest)} except the * request is obtained through {@link RequestContextHolder}. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromCurrentServletMapping() { return fromServletMapping(getCurrentRequest()); @@ -171,6 +198,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Same as {@link #fromRequestUri(HttpServletRequest)} except the * request is obtained through {@link RequestContextHolder}. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromCurrentRequestUri() { return fromRequestUri(getCurrentRequest()); @@ -179,6 +209,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Same as {@link #fromRequest(HttpServletRequest)} except the * request is obtained through {@link RequestContextHolder}. + * + *

Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. */ public static ServletUriComponentsBuilder fromCurrentRequest() { return fromRequest(getCurrentRequest()); diff --git a/src/docs/asciidoc/web/web-mvc.adoc b/src/docs/asciidoc/web/web-mvc.adoc index d891070dfe1..41ddbc2370f 100644 --- a/src/docs/asciidoc/web/web-mvc.adoc +++ b/src/docs/asciidoc/web/web-mvc.adoc @@ -3436,6 +3436,38 @@ with a base URL and then use the instance-based "withXxx" methods. For example: ---- +[[mvc-links-to-controllers-forwarded-headers]] +=== Working with "Forwarded" and "X-Forwarded-*" Headers + +As a request goes through proxies such as load balancers the host, port, and +scheme may change presenting a challenge for applications that need to create links +to resources since the links should reflect the host, port, and scheme of the +original request as seen from a client perspective. + +https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header +for proxies to use to provide information about the original request. There are also +other non-standard headers in use such as "X-Forwarded-Host", "X-Forwarded-Port", +and "X-Forwarded-Proto". + +Both `ServletUriComponentsBuilder` and `MvcUriComponentsBuilder` detect, extract, and use +information from the "Forwarded" header, or from "X-Forwarded-Host", "X-Forwarded-Port", +and "X-Forwarded-Proto" if "Forwarded" is not present, so that the resulting links reflect +the original request. + +The `ForwardedHeaderFilter` provides an alternative to do the same once and globally for +the entire application. The filter wraps the request in order to overlay host, port, and +scheme information and also "hides" any forwarded headers for subsequent processing. + +Note that there are security considerations when using forwarded headers as explained +in Section 8 of RFC 7239. At the application level it is difficult to determine whether +forwarded headers can be trusted or not. This is why the network upstream should be +configured correctly to filter out untrusted forwarded headers from the outside. + +Applications that don't have a proxy and don't need to use forwarded headers can +configure the `ForwardedHeaderFilter` to remove and ignore such headers. + + + [[mvc-links-to-controllers-from-views]] === Building URIs to Controllers and methods from views