diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java index 25230c45d8..d454017344 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java @@ -55,6 +55,11 @@ public class SecurityProperties implements SecurityPrequisite { */ public static final int IGNORED_ORDER = Ordered.HIGHEST_PRECEDENCE; + /** + * The default order of Spring Security's Filter + */ + public static final int DEFAULT_FILTER_ORDER = 0; + private boolean requireSsl; // Flip this when session creation is disabled by default @@ -70,6 +75,8 @@ public class SecurityProperties implements SecurityPrequisite { private final User user = new User(); + private int filterOrder = DEFAULT_FILTER_ORDER; + public Headers getHeaders() { return this.headers; } @@ -118,6 +125,14 @@ public class SecurityProperties implements SecurityPrequisite { return this.ignored; } + public int getFilterOrder() { + return this.filterOrder; + } + + public void setFilterOrder(int filterOrder) { + this.filterOrder = filterOrder; + } + public static class Headers { public static enum HSTS { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.java index ceb033e5f3..8c40061fe3 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.java @@ -20,10 +20,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.servlet.Filter; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; @@ -32,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers; import org.springframework.boot.autoconfigure.web.ErrorController; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -48,6 +52,7 @@ import org.springframework.security.config.annotation.web.configurers.HeadersCon import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; import org.springframework.security.web.header.writers.HstsHeaderWriter; import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -96,6 +101,18 @@ public class SpringBootWebSecurityConfiguration { return new IgnoredPathsWebSecurityConfigurerAdapter(); } + @Bean + @ConditionalOnBean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) + public FilterRegistrationBean securityFilterChainRegistration( + @Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) Filter securityFilter, + SecurityProperties securityProperties) { + FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter); + registration.setOrder(securityProperties.getFilterOrder()); + registration + .setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME); + return registration; + } + public static void configureHeaders(HeadersConfigurer configurer, SecurityProperties.Headers headers) throws Exception { if (headers.getHsts() != Headers.HSTS.NONE) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java index 112a6e03f4..76b8c467f6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.test.City; import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; +import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @@ -82,6 +83,35 @@ public class SecurityAutoConfigurationTests { assertEquals(5, filterChains.size()); } + @Test + public void testDefaultFilterOrder() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(SecurityAutoConfiguration.class, + ServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertEquals( + 0, + this.context.getBean("securityFilterChainRegistration", + FilterRegistrationBean.class).getOrder()); + } + + @Test + public void testCustomFilterOrder() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, "security.filter-order:12345"); + this.context.setServletContext(new MockServletContext()); + this.context.register(SecurityAutoConfiguration.class, + ServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertEquals( + 12345, + this.context.getBean("securityFilterChainRegistration", + FilterRegistrationBean.class).getOrder()); + } + @Test public void testDisableIgnoredStaticApplicationPaths() throws Exception { this.context = new AnnotationConfigWebApplicationContext(); diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 264545ce1c..a8ea967da6 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -168,6 +168,7 @@ content into your application; rather pick only the properties that you need. security.basic.enabled=true security.basic.realm=Spring security.basic.path= # /** + security.filter-order=0 security.headers.xss=false security.headers.cache=false security.headers.frame=false