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
This commit is contained in:
Andy Wilkinson 2014-10-30 13:29:27 +00:00
parent cf24af0bfb
commit 0cdb1d3f22
4 changed files with 63 additions and 0 deletions

View File

@ -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 {

View File

@ -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) {

View File

@ -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();

View File

@ -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