From 0cdb1d3f22307b84887797a0f059ee0c9357716e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Oct 2014 13:29:27 +0000 Subject: [PATCH] Make Spring Security's filter's order configurable and default to zero Previously, Spring Security's filter had no configured order. Due to the use of AnnotationAwareOrderComparater this meant that its order defaulted to LOWEST_PRECEDENCE. This meant that a user had to declare a FilterRegistrationBean for the filter and explicitly set its order if they want another filter to run after Spring Security's. This commit updates the security auto-configuration to assign a default order of zero to Spring Security's filter, allowing filters to be easily configured to run before it or after it. This default value can overridden using the server.filter-order property. The default order is also exposed as a constant on SecurityProperties, allowing it to be referenced from other filter declarations. Closes gh-1640 --- .../security/SecurityProperties.java | 15 ++++++++++ .../SpringBootWebSecurityConfiguration.java | 17 +++++++++++ .../SecurityAutoConfigurationTests.java | 30 +++++++++++++++++++ .../appendix-application-properties.adoc | 1 + 4 files changed, 63 insertions(+) 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