From ff0daa8d5c9d85b91fa02cd9c254afd14aadf0d9 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 13 Oct 2015 23:10:59 -0700 Subject: [PATCH] Use DelegatingFilterProxy for Spring Security Update SecurityFilterAutoConfiguration to use a DelegatingFilterProxy filter rather directly referencing the springSecurityFilterChain bean. Using a DelegatingFilterProxy helps to prevent early initialization of beans and makes Spring Security work in a similar to way to if were installed in a regular WAR deployment. Fixes gh-4154 --- .../SecurityFilterAutoConfiguration.java | 15 +- .../SecurityAutoConfigurationTests.java | 10 +- ...ConfigurationEarlyInitializationTests.java | 166 ++++++++++++++++++ 3 files changed, 178 insertions(+), 13 deletions(-) create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfigurationEarlyInitializationTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfiguration.java index e2ab0895611..ef5a1d53bac 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfiguration.java @@ -16,15 +16,13 @@ package org.springframework.boot.autoconfigure.security; -import javax.servlet.Filter; - -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.context.embedded.FilterRegistrationBean; +import org.springframework.boot.context.embedded.DelegatingFilterProxyRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration; @@ -37,6 +35,7 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn * {@link WebSecurityConfiguration} exists. * * @author Rob Winch + * @author Phillip Webb * @since 1.3 */ @Configuration @@ -49,12 +48,12 @@ public class SecurityFilterAutoConfiguration { @Bean @ConditionalOnBean(name = DEFAULT_FILTER_NAME) - public FilterRegistrationBean securityFilterChainRegistration( - @Qualifier(DEFAULT_FILTER_NAME) Filter securityFilter, + public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration( + ApplicationContext applicationContext, SecurityProperties securityProperties) { - FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter); + DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean( + DEFAULT_FILTER_NAME); registration.setOrder(securityProperties.getFilterOrder()); - registration.setName(DEFAULT_FILTER_NAME); return registration; } 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 77c3903c7dd..0ee93e267ad 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.DelegatingFilterProxyRegistrationBean; import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.ApplicationEvent; @@ -106,7 +107,7 @@ public class SecurityAutoConfigurationTests { this.context.refresh(); assertEquals(FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100, this.context.getBean("securityFilterChainRegistration", - FilterRegistrationBean.class).getOrder()); + DelegatingFilterProxyRegistrationBean.class).getOrder()); } @Test @@ -136,7 +137,7 @@ public class SecurityAutoConfigurationTests { this.context.refresh(); assertEquals(FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100, this.context.getBean("securityFilterChainRegistration", - FilterRegistrationBean.class).getOrder()); + DelegatingFilterProxyRegistrationBean.class).getOrder()); } @Test @@ -149,9 +150,8 @@ public class SecurityAutoConfigurationTests { ServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); - assertEquals(12345, this.context - .getBean("securityFilterChainRegistration", FilterRegistrationBean.class) - .getOrder()); + assertEquals(12345, this.context.getBean("securityFilterChainRegistration", + DelegatingFilterProxyRegistrationBean.class).getOrder()); } @Test diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfigurationEarlyInitializationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfigurationEarlyInitializationTests.java new file mode 100644 index 00000000000..cee40c9d24a --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityFilterAutoConfigurationEarlyInitializationTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2012-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.security; + +import java.io.IOException; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfigurationTests.WebSecurity; +import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; +import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; +import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.boot.test.TestRestTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * Integration test to ensure {@link SecurityFilterAutoConfiguration} doesn't cause early + * initialization. + * + * @author Phillip Webb + */ +public class SecurityFilterAutoConfigurationEarlyInitializationTests { + + // gh-4154 + + @Test + public void testSecurityFilterDoesNotCauseEarlyInitialization() throws Exception { + AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext(); + try { + EnvironmentTestUtils.addEnvironment(context, "server.port:0", + "security.user.password:password"); + context.register(Config.class); + context.refresh(); + int port = context.getEmbeddedServletContainer().getPort(); + new TestRestTemplate("user", "password") + .getForEntity("http://localhost:" + port, Object.class); + // If early initialization occurred a ConverterNotFoundException is thrown + + } + finally { + context.close(); + } + + } + + @Configuration + @Import({ DeserializerBean.class, JacksonModuleBean.class, ExampleController.class, + ConverterBean.class }) + @ImportAutoConfiguration({ WebMvcAutoConfiguration.class, + JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, + DispatcherServletAutoConfiguration.class, WebSecurity.class, + SecurityAutoConfiguration.class, SecurityFilterAutoConfiguration.class, + ServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class }) + static class Config { + + @Bean + public TomcatEmbeddedServletContainerFactory containerFactory() { + TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(); + factory.setPort(0); + return factory; + } + + } + + public static class SourceType { + + public String foo; + + } + + public static class DestinationType { + + public String bar; + + } + + @Component + public static class JacksonModuleBean extends SimpleModule { + + private static final long serialVersionUID = 1L; + + @Autowired + public JacksonModuleBean(DeserializerBean myDeser) { + addDeserializer(SourceType.class, myDeser); + } + + } + + @Component + public static class DeserializerBean extends StdDeserializer { + + @Autowired + ConversionService conversionService; + + public DeserializerBean() { + super(SourceType.class); + } + + @Override + public SourceType deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + return new SourceType(); + } + + } + + @RestController + public static class ExampleController { + + @Autowired + private ConversionService conversionService; + + @RequestMapping("/") + public void convert() { + System.out.println("Hello"); + this.conversionService.convert(new SourceType(), DestinationType.class); + } + + } + + @Component + public static class ConverterBean implements Converter { + + @Override + public DestinationType convert(SourceType source) { + return new DestinationType(); + } + + } + +}