Merge branch '1.1.x'

This commit is contained in:
Dave Syer 2014-09-13 06:44:03 -05:00
commit 389c4f3e0b
4 changed files with 117 additions and 65 deletions

View File

@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties.User; import org.springframework.boot.autoconfigure.security.SecurityProperties.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -41,9 +42,15 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter; import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.stereotype.Component;
/** /**
* Configuration for a Spring Security in-memory {@link AuthenticationManager}. * Configuration for a Spring Security in-memory {@link AuthenticationManager}. Can be
* disabled by providing a bean of type AuthenticationManager. The value provided by this
* configuration will become the "global" authentication manager (from Spring Security),
* or the parent of the global instance. Thus it acts as a fallback when no others are
* provided, is used by method security if enabled, and as a parent authentication manager
* for "local" authentication managers in individual filter chains.
* *
* @author Dave Syer * @author Dave Syer
* @author Rob Winch * @author Rob Winch
@ -53,8 +60,15 @@ import org.springframework.security.config.annotation.authentication.configurers
@ConditionalOnMissingBean(AuthenticationManager.class) @ConditionalOnMissingBean(AuthenticationManager.class)
@Order(Ordered.LOWEST_PRECEDENCE - 3) @Order(Ordered.LOWEST_PRECEDENCE - 3)
public class AuthenticationManagerConfiguration extends public class AuthenticationManagerConfiguration extends
GlobalAuthenticationConfigurerAdapter implements GlobalAuthenticationConfigurerAdapter {
ApplicationListener<ContextRefreshedEvent> {
/*
* Yes, this class is a GlobalAuthenticationConfigurerAdapter, even though none of
* those methods are overridden: we want Spring Security to instantiate us early, so
* we can in turn force the SecurityPrequisites to be instantiated. This will prevent
* ordering issues between Spring Boot modules when they need to influence the default
* security configuration.
*/
private static Log logger = LogFactory private static Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class); .getLog(AuthenticationManagerConfiguration.class);
@ -63,44 +77,49 @@ public class AuthenticationManagerConfiguration extends
private List<SecurityPrequisite> dependencies; private List<SecurityPrequisite> dependencies;
@Autowired @Autowired
private ObjectPostProcessor<Object> objectPostProcessor; private SecurityProperties security;
@Autowired @Autowired
private SecurityProperties security; private ObjectPostProcessor<Object> objectPostProcessor;
@Bean
@Primary
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth)
throws Exception {
/*
* This AuthenticationManagerBuilder is for the global AuthenticationManager
*/
BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
configurer.init(auth);
configurer.configure(auth);
AuthenticationManager manager = configurer.getAuthenticationManagerBuilder()
.getOrBuild();
configurer.configureParent(auth);
return manager;
}
@Component
protected static class AuthenticationManagerConfigurationListener implements
ApplicationListener<ContextRefreshedEvent> {
@Autowired @Autowired
private AuthenticationEventPublisher authenticationEventPublisher; private AuthenticationEventPublisher authenticationEventPublisher;
private BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(this.configurer);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
this.configurer.configureParent(auth);
}
@Bean
@Primary
public AuthenticationManager authenticationManager() {
AuthenticationManager manager = this.configurer.getAuthenticationManagerBuilder()
.getOrBuild();
return manager;
}
@Override @Override
public void onApplicationEvent(ContextRefreshedEvent event) { public void onApplicationEvent(ContextRefreshedEvent event) {
AuthenticationManager manager = this.configurer.getAuthenticationManagerBuilder() ApplicationContext context = event.getApplicationContext();
.getOrBuild(); if (context.getBeanNamesForType(AuthenticationManager.class).length == 0) {
return;
}
AuthenticationManager manager = context.getBean(AuthenticationManager.class);
if (manager instanceof ProviderManager) { if (manager instanceof ProviderManager) {
((ProviderManager) manager) ((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher); .setAuthenticationEventPublisher(this.authenticationEventPublisher);
} }
} }
}
/** /**
* We must add {@link BootDefaultingAuthenticationConfigurerAdapter} in the init phase * We must add {@link BootDefaultingAuthenticationConfigurerAdapter} in the init phase
* of the last {@link GlobalAuthenticationConfigurerAdapter}. The reason is that the * of the last {@link GlobalAuthenticationConfigurerAdapter}. The reason is that the
@ -163,7 +182,8 @@ public class AuthenticationManagerConfiguration extends
.roles(roles.toArray(new String[roles.size()])).and().and().build(); .roles(roles.toArray(new String[roles.size()])).and().and().build();
// Defer actually setting the parent on the AuthenticationManagerBuilder // Defer actually setting the parent on the AuthenticationManagerBuilder
// because it makes it "configured" and we are only in the init() phase here. // because it makes it "configured" and we are only in the init() phase
// here.
} }
} }

View File

@ -18,15 +18,29 @@ package org.springframework.boot.autoconfigure.web;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.web.BasicErrorControllerMockMvcTests.MinimalWebConfiguration;
import org.springframework.boot.autoconfigure.web.BasicErrorControllerMockMvcTests.TestConfiguration;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.TestRestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.AbstractView;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
@ -40,8 +54,9 @@ import static org.junit.Assert.assertThat;
* @author Dave Syer * @author Dave Syer
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BasicErrorControllerMockMvcTests.TestConfiguration.class) @SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration @WebAppConfiguration
@DirtiesContext
@IntegrationTest("server.port=0") @IntegrationTest("server.port=0")
public class BasicErrorControllerIntegrationTest { public class BasicErrorControllerIntegrationTest {
@ -69,4 +84,49 @@ public class BasicErrorControllerIntegrationTest {
assertThat(resp, containsString("errors=[{codes=")); assertThat(resp, containsString("errors=[{codes="));
assertThat(resp, containsString("org.springframework.validation.BindException")); assertThat(resp, containsString("org.springframework.validation.BindException"));
} }
@Configuration
@MinimalWebConfiguration
public static class TestConfiguration {
// For manual testing
public static void main(String[] args) {
SpringApplication.run(TestConfiguration.class, args);
}
@Bean
public View error() {
return new AbstractView() {
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
response.getWriter().write("ERROR_BEAN");
}
};
}
@RestController
protected static class Errors {
public String getFoo() {
return "foo";
}
@RequestMapping("/")
public String home() {
throw new IllegalStateException("Expected!");
}
@RequestMapping("/bind")
public String bind() throws Exception {
BindException error = new BindException(this, "test");
error.rejectValue("foo", "bar.error");
throw error;
}
}
}
} }

View File

@ -23,18 +23,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId> <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>

View File

@ -19,18 +19,14 @@ package sample.ui.secure;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.Ordered; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -65,16 +61,7 @@ public class SampleWebSecureApplication extends WebMvcConfigurerAdapter {
registry.addViewController("/login").setViewName("login"); registry.addViewController("/login").setViewName("login");
} }
@Bean @Configuration
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
@Bean
public AuthenticationSecurity authenticationSecurity() {
return new AuthenticationSecurity();
}
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter { protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@ -86,21 +73,14 @@ public class SampleWebSecureApplication extends WebMvcConfigurerAdapter {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin() http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
.loginPage("/login").failureUrl("/login?error").permitAll(); .loginPage("/login").failureUrl("/login?error").permitAll();
} }
}
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
protected static class AuthenticationSecurity extends
GlobalAuthenticationConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override @Override
public void init(AuthenticationManagerBuilder auth) throws Exception { public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(this.dataSource).withUser("admin") auth.inMemoryAuthentication().withUser("admin").password("admin")
.password("admin").roles("ADMIN", "USER").and().withUser("user") .roles("ADMIN", "USER").and().withUser("user").password("user")
.password("user").roles("USER"); .roles("USER");
} }
} }
} }