diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java index fc3d9c1380..a7d18cddf8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,15 +85,33 @@ public class ServletWebServerFactoryAutoConfiguration { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } - @Bean - @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) + @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework") - public FilterRegistrationBean forwardedHeaderFilter() { - ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); - FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); - registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR); - registration.setOrder(Ordered.HIGHEST_PRECEDENCE); - return registration; + static class ForwardedHeaderFilterConfiguration { + + @Bean + @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") + @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) + public FilterRegistrationBean tomcatForwardedHeaderFilter( + ServerProperties serverProperties) { + return createForwardedHeaderFilter(serverProperties.getTomcat().isUseRelativeRedirects()); + } + + @Bean + @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) + public FilterRegistrationBean defaultForwardedHeaderFilter() { + return createForwardedHeaderFilter(false); + } + + private FilterRegistrationBean createForwardedHeaderFilter(boolean relativeRedirects) { + ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); + filter.setRelativeRedirects(relativeRedirects); + FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); + registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR); + registration.setOrder(Ordered.HIGHEST_PRECEDENCE); + return registration; + } + } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java index 0338bcdd16..ffdc3071a1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java @@ -57,6 +57,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.filter.ForwardedHeaderFilter; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.FrameworkServlet; @@ -368,6 +369,36 @@ class ServletWebServerFactoryAutoConfigurationTests { .run((context) -> assertThat(context).hasSingleBean(FilterRegistrationBean.class)); } + @Test + void relativeRedirectsShouldBeEnabledWhenUsingTomcatContainerAndUseRelativeRedirects() { + WebApplicationContextRunner runner = new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class)) + .withPropertyValues("server.forward-headers-strategy=framework", + "server.tomcat.use-relative-redirects=true"); + + runner.run((context) -> { + Filter filter = context.getBean(FilterRegistrationBean.class).getFilter(); + Boolean relativeRedirects = (Boolean) ReflectionTestUtils.getField(filter, "relativeRedirects"); + assertThat(relativeRedirects).isTrue(); + }); + } + + @Test + void relativeRedirectsShouldNotBeEnabledWhenNotUsingTomcatContainer() { + WebApplicationContextRunner runner = new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withClassLoader(new FilteredClassLoader(Tomcat.class)) + .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class)) + .withPropertyValues("server.forward-headers-strategy=framework"); + + runner.run((context) -> { + Filter filter = context.getBean(FilterRegistrationBean.class).getFilter(); + Boolean relativeRedirects = (Boolean) ReflectionTestUtils.getField(filter, "relativeRedirects"); + assertThat(relativeRedirects).isFalse(); + }); + } + private ContextConsumer verifyContext() { return this::verifyContext; }