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..0537653e99 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,35 @@ 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; + @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) + static class ForwardedHeaderFilterConfiguration { + + @Bean + @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") + @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) + ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) { + return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects()); + } + + @Bean + FilterRegistrationBean forwardedHeaderFilter( + ObjectProvider customizerProvider) { + ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); + customizerProvider.ifAvailable((customizer) -> customizer.customize(filter)); + FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); + registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR); + registration.setOrder(Ordered.HIGHEST_PRECEDENCE); + return registration; + } + + } + + interface ForwardedHeaderFilterCustomizer { + + void customize(ForwardedHeaderFilter filter); + } /** 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..c30bc1c0cf 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 @@ -352,6 +352,7 @@ class ServletWebServerFactoryAutoConfigurationTests { assertThat(context).hasSingleBean(FilterRegistrationBean.class); Filter filter = context.getBean(FilterRegistrationBean.class).getFilter(); assertThat(filter).isInstanceOf(ForwardedHeaderFilter.class); + assertThat(filter).extracting("relativeRedirects").isEqualTo(false); }); } @@ -368,6 +369,48 @@ 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(); + assertThat(filter).isInstanceOf(ForwardedHeaderFilter.class); + assertThat(filter).extracting("relativeRedirects").isEqualTo(true); + }); + } + + @Test + void relativeRedirectsShouldNotBeEnabledWhenUsingTomcatContainerAndNotUsingRelativeRedirects() { + WebApplicationContextRunner runner = new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class)) + .withPropertyValues("server.forward-headers-strategy=framework", + "server.tomcat.use-relative-redirects=false"); + runner.run((context) -> { + Filter filter = context.getBean(FilterRegistrationBean.class).getFilter(); + assertThat(filter).isInstanceOf(ForwardedHeaderFilter.class); + assertThat(filter).extracting("relativeRedirects").isEqualTo(false); + }); + } + + @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(); + assertThat(filter).isInstanceOf(ForwardedHeaderFilter.class); + assertThat(filter).extracting("relativeRedirects").isEqualTo(false); + }); + } + private ContextConsumer verifyContext() { return this::verifyContext; }