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 Note: This method extracts values from "Forwarded"
+ * and "X-Forwarded-*" headers if found. See class-level docs.
* @param controllerType the target controller
*/
public static 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