mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Default use-relative-redirects to true
Spring Boot unconditionally set useRelativeRedirects on the Tomcat Context, defaulting it to false. That overrode Tomcat's own default and forced absolute Location headers on every sendRedirect. Keep server.tomcat.use-relative-redirects a simple boolean, but default it to true so that relative Location headers are used out of the box. The property can still be set to false to opt back into absolute redirects. Smoke tests that asserted a port-qualified absolute Location are updated to the relative form, and the proxy tip in the reference documentation is qualified since the context root redirect no longer carries a scheme. Signed-off-by: Tiziano Basile <tiz.basile@gmail.com> See gh-51173
This commit is contained in:
committed by
Andy Wilkinson
parent
a07c43466b
commit
f119e58767
@@ -562,8 +562,9 @@ server:
|
||||
|
||||
NOTE: You can trust all proxies by setting the `internal-proxies` to empty (but do not do so in production).
|
||||
|
||||
TIP: If you are using Tomcat and terminating SSL at the proxy, configprop:server.tomcat.redirect-context-root[] should be set to `false`.
|
||||
TIP: If you are using Tomcat, terminating SSL at the proxy, and have set configprop:server.tomcat.use-relative-redirects[] to `false`, then configprop:server.tomcat.redirect-context-root[] should also be set to `false`.
|
||||
This allows the `X-Forwarded-Proto` header to be honored before any redirects are performed.
|
||||
When relative redirects are in use, which is Tomcat's default, the context root redirect carries no scheme so there is nothing for the header to correct.
|
||||
|
||||
You can take complete control of the configuration of Tomcat's javadoc:org.apache.catalina.valves.RemoteIpValve[] by switching the automatic one off (to do so, set `server.forward-headers-strategy=NONE`) and adding a new valve instance using a javadoc:org.springframework.boot.web.server.WebServerFactoryCustomizer[] bean.
|
||||
|
||||
|
||||
+3
-2
@@ -56,6 +56,7 @@ import org.springframework.util.unit.DataSize;
|
||||
* @author Florian Storz
|
||||
* @author Michael Weidmann
|
||||
* @author Lasse Wulff
|
||||
* @author Tiziano Basile
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("server.tomcat")
|
||||
@@ -103,9 +104,9 @@ public class TomcatServerProperties {
|
||||
|
||||
/**
|
||||
* Whether HTTP 1.1 and later location headers generated by a call to sendRedirect
|
||||
* will use relative or absolute redirects.
|
||||
* will use relative or absolute redirects. Has no effect on a reactive web server.
|
||||
*/
|
||||
private boolean useRelativeRedirects;
|
||||
private boolean useRelativeRedirects = true;
|
||||
|
||||
/**
|
||||
* Character encoding to use to decode the URI.
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
* @author Tiziano Basile
|
||||
*/
|
||||
class TomcatServletWebServerFactoryCustomizer
|
||||
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {
|
||||
|
||||
+3
-2
@@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link TomcatServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Tiziano Basile
|
||||
*/
|
||||
class TomcatServerPropertiesTests {
|
||||
|
||||
@@ -235,8 +236,8 @@ class TomcatServerPropertiesTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatUseRelativeRedirectsDefaultsToFalse() {
|
||||
assertThat(this.properties.isUseRelativeRedirects()).isFalse();
|
||||
void tomcatUseRelativeRedirectsDefaultsToTrue() {
|
||||
assertThat(this.properties.isUseRelativeRedirects()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+11
-2
@@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link TomcatServletWebServerFactoryCustomizer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Tiziano Basile
|
||||
*/
|
||||
class TomcatServletWebServerFactoryCustomizerTests {
|
||||
|
||||
@@ -80,14 +81,22 @@ class TomcatServletWebServerFactoryCustomizerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void useRelativeRedirectsCanBeConfigured() {
|
||||
bind("server.tomcat.use-relative-redirects=true");
|
||||
void useRelativeRedirectsDefaultsToTrue() {
|
||||
assertThat(this.tomcatProperties.isUseRelativeRedirects()).isTrue();
|
||||
TomcatWebServer server = customizeAndGetServer();
|
||||
Context context = (Context) server.getTomcat().getHost().findChildren()[0];
|
||||
assertThat(context.getUseRelativeRedirects()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void useRelativeRedirectsCanBeDisabled() {
|
||||
bind("server.tomcat.use-relative-redirects=false");
|
||||
assertThat(this.tomcatProperties.isUseRelativeRedirects()).isFalse();
|
||||
TomcatWebServer server = customizeAndGetServer();
|
||||
Context context = (Context) server.getTomcat().getHost().findChildren()[0];
|
||||
assertThat(context.getUseRelativeRedirects()).isFalse();
|
||||
}
|
||||
|
||||
private void bind(String... inlinedProperties) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server.tomcat",
|
||||
|
||||
+2
-2
@@ -113,7 +113,7 @@ class SampleOAuth2AuthorizationServerApplicationTests {
|
||||
void anonymousShouldRedirectToLogin() {
|
||||
RestTestClient.ResponseSpec response = nonFollowingRedirect().get().uri("/").exchange();
|
||||
response.expectStatus().isFound();
|
||||
response.expectHeader().location("http://localhost:" + this.port + "/login");
|
||||
response.expectHeader().location("/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -182,7 +182,7 @@ class SampleOAuth2AuthorizationServerApplicationTests {
|
||||
.body(body)
|
||||
.exchange();
|
||||
response.expectStatus().isFound();
|
||||
response.expectHeader().location("http://localhost:" + this.port + "/login");
|
||||
response.expectHeader().location("/login");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ class SampleOAuth2ClientApplicationTests {
|
||||
void everythingShouldRedirectToLogin() {
|
||||
RestTestClient.ResponseSpec response = nonFollowingRedirect().get().uri("/").exchange();
|
||||
response.expectStatus().isFound();
|
||||
response.expectHeader().location("http://localhost:" + this.port + "/login");
|
||||
response.expectHeader().location("/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ class SampleSaml2RelyingPartyApplicationTests {
|
||||
void everythingShouldRedirectToLogin() {
|
||||
RestTestClient.ResponseSpec response = nonFollowingRedirect().get().uri("/").exchange();
|
||||
response.expectStatus().isFound();
|
||||
response.expectHeader().location("http://localhost:" + this.port + "/login");
|
||||
response.expectHeader().location("/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-5
@@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureRestTestClient;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -38,9 +37,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@AutoConfigureRestTestClient
|
||||
class SampleGroovyTemplateApplicationTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private RestTestClient restTestClient;
|
||||
|
||||
@@ -62,7 +58,7 @@ class SampleGroovyTemplateApplicationTests {
|
||||
.body(map)
|
||||
.exchange()
|
||||
.expectHeader()
|
||||
.value("Location", (location) -> assertThat(location).contains("localhost:" + this.port));
|
||||
.value("Location", (location) -> assertThat(location).matches("/\\d+(;jsessionid=[\\w.]+)?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ class SampleMethodSecurityApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/");
|
||||
assertThat(location.toString()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-2
@@ -71,7 +71,7 @@ class SampleWebSecureCustomApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/login");
|
||||
assertThat(location.toString()).isEqualTo("/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +99,7 @@ class SampleWebSecureCustomApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/");
|
||||
assertThat(location.toString()).isEqualTo("/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,7 +71,7 @@ class SampleWebSecureJdbcApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/login");
|
||||
assertThat(location.toString()).isEqualTo("/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +99,7 @@ class SampleWebSecureJdbcApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/");
|
||||
assertThat(location.toString()).isEqualTo("/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -78,7 +78,7 @@ class SampleWebSecureApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/login");
|
||||
assertThat(location.toString()).isEqualTo("/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,7 +106,7 @@ class SampleWebSecureApplicationTests {
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FOUND);
|
||||
URI location = result.getResponseHeaders().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).endsWith(this.port + "/");
|
||||
assertThat(location.toString()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@org.springframework.boot.test.context.TestConfiguration(proxyBeanMethods = false)
|
||||
|
||||
+1
-5
@@ -24,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureRestTestClient;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -43,9 +42,6 @@ class SampleWebUiApplicationTests {
|
||||
@Autowired
|
||||
private RestTestClient restTestClient;
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Test
|
||||
void testHome() {
|
||||
this.restTestClient.get().uri("/").exchangeSuccessfully().expectBody(String.class).value((body) -> {
|
||||
@@ -67,7 +63,7 @@ class SampleWebUiApplicationTests {
|
||||
.getResponseHeaders()
|
||||
.getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat(location.toString()).contains("localhost:" + this.port);
|
||||
assertThat(location.toString()).matches("/\\d+(;jsessionid=[\\w.]+)?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user