diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/BootGlobalAuthenticationConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/BootGlobalAuthenticationConfiguration.java new file mode 100644 index 00000000000..a0568568101 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/BootGlobalAuthenticationConfiguration.java @@ -0,0 +1,69 @@ +/* + * 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.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter; + +/** + * This works with the {@link AuthenticationConfiguration} to ensure that users are able to use: + * + *
+ * public void configureGlobal(AuthenticationManagerBuilder auth) {
+ *     ...
+ * }
+ * 
+ * + * within their classes annotated with {{@EnableAutoConfiguration}} or use {{@SpringBootApplication}}. + * + * @author Rob Winch + */ +@Configuration +@ConditionalOnClass(GlobalAuthenticationConfigurerAdapter.class) +public class BootGlobalAuthenticationConfiguration { + + @Bean + public static BootGlobalAuthenticationConfigurationAdapter bootGlobalAuthenticationConfigurationAdapter(ApplicationContext context) { + return new BootGlobalAuthenticationConfigurationAdapter(context); + } + + private static class BootGlobalAuthenticationConfigurationAdapter extends GlobalAuthenticationConfigurerAdapter { + private final ApplicationContext context; + private static final Log logger = LogFactory.getLog(BootGlobalAuthenticationConfiguration.class); + + public BootGlobalAuthenticationConfigurationAdapter(ApplicationContext context) { + this.context = context; + } + + @Override + public void init(AuthenticationManagerBuilder auth) { + Map beansWithAnnotation = context.getBeansWithAnnotation(EnableAutoConfiguration.class); + if(logger.isDebugEnabled()) { + logger.debug("Eagerly initializing " + beansWithAnnotation); + } + } + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java index aa462f3bf0b..71913f28067 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * 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. @@ -49,7 +49,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur GlobalAuthenticationConfigurerAdapter.class }) @EnableConfigurationProperties @Import({ SpringBootWebSecurityConfiguration.class, - AuthenticationManagerConfiguration.class }) + AuthenticationManagerConfiguration.class, + BootGlobalAuthenticationConfiguration.class}) public class SecurityAutoConfiguration { @Bean diff --git a/spring-boot-full-build/pom.xml b/spring-boot-full-build/pom.xml index 1e3b64c611f..3e14110538f 100644 --- a/spring-boot-full-build/pom.xml +++ b/spring-boot-full-build/pom.xml @@ -57,6 +57,7 @@ ../spring-boot-samples ../spring-boot-deployment-tests ../spring-boot-integration-tests + ../spring-boot-security-tests ../spring-boot-docs diff --git a/spring-boot-security-tests/pom.xml b/spring-boot-security-tests/pom.xml new file mode 100644 index 00000000000..0a966975a0a --- /dev/null +++ b/spring-boot-security-tests/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-parent + 1.2.2.BUILD-SNAPSHOT + ../spring-boot-parent + + spring-boot-security-tests + pom + Spring Boot Security Tests + ${project.name} + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + spring-boot-security-tests-web-helloworld + + diff --git a/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/pom.xml b/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/pom.xml new file mode 100644 index 00000000000..591d762a0c1 --- /dev/null +++ b/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-security-tests + 1.2.2.BUILD-SNAPSHOT + ../ + + spring-boot-security-tests-web-helloworld + Spring Boot Security Tests - Web Basic + ${project.name} + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + diff --git a/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/src/main/java/sample/HelloWebSecurityApplication.java b/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/src/main/java/sample/HelloWebSecurityApplication.java new file mode 100644 index 00000000000..dacf1ea7629 --- /dev/null +++ b/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/src/main/java/sample/HelloWebSecurityApplication.java @@ -0,0 +1,39 @@ +/* + * 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 sample; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; + +@SpringBootApplication +public class HelloWebSecurityApplication { + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) + throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + // @formatter:on + } + + public static void main(String[] args) { + SpringApplication.run(HelloWebSecurityApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/src/test/java/sample/HelloWebSecurityApplicationTests.java b/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/src/test/java/sample/HelloWebSecurityApplicationTests.java new file mode 100644 index 00000000000..284c809f5cf --- /dev/null +++ b/spring-boot-security-tests/spring-boot-security-tests-web-helloworld/src/test/java/sample/HelloWebSecurityApplicationTests.java @@ -0,0 +1,72 @@ +/* + * 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 sample; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = HelloWebSecurityApplication.class) +@WebIntegrationTest(randomPort = true) +public class HelloWebSecurityApplicationTests { + @Autowired + FilterChainProxy springSecurityFilterChain; + + MockHttpServletRequest request; + + MockHttpServletResponse response; + + MockFilterChain chain; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + chain = new MockFilterChain(); + } + + @Test + public void requiresAuthentication() throws Exception { + springSecurityFilterChain.doFilter(request, response, chain); + + assertThat(response.getStatus(), equalTo(HttpServletResponse.SC_UNAUTHORIZED)); + } + + + @Test + public void userAuthenticates() throws Exception { + request.addHeader("Authorization", "Basic " + new String(Base64.encode("user:password".getBytes("UTF-8")))); + + springSecurityFilterChain.doFilter(request, response, chain); + + assertThat(response.getStatus(), equalTo(HttpServletResponse.SC_OK)); + } +}