Refactor Spring Security auto-configuration classes
The auto-configuration for a `UserDetailsService` and the web bits has been split into two. Closes gh-11915 Fixes gh-11891
This commit is contained in:
@@ -17,29 +17,21 @@
|
||||
package org.springframework.boot.autoconfigure.security.reactive;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security in a reactive
|
||||
* application. This auto-configuration adds {@link EnableWebFluxSecurity} and delegates
|
||||
* to Spring Security's content-negotiation mechanism for authentication. In a webapp this
|
||||
* configuration also secures all web endpoints.
|
||||
* application.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ EnableWebFluxSecurity.class,
|
||||
AuthenticationPrincipalArgumentResolver.class })
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
@Import({ WebFluxSecurityConfiguration.class,
|
||||
ReactiveAuthenticationManagerConfiguration.class })
|
||||
@Import(WebFluxSecurityConfiguration.class)
|
||||
public class ReactiveSecurityAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
@ConditionalOnMissingBean({ ReactiveAuthenticationManager.class,
|
||||
ReactiveUserDetailsService.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
class ReactiveAuthenticationManagerConfiguration {
|
||||
public class ReactiveUserDetailsServiceAutoConfiguration {
|
||||
|
||||
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
|
||||
|
||||
@@ -57,7 +57,7 @@ class ReactiveAuthenticationManagerConfiguration {
|
||||
.compile("^\\{.+}.*$");
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(ReactiveAuthenticationManagerConfiguration.class);
|
||||
.getLog(ReactiveUserDetailsServiceAutoConfiguration.class);
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService reactiveUserDetailsService(
|
||||
@@ -24,8 +24,9 @@ import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
|
||||
/**
|
||||
* Switches on {@link EnableWebFluxSecurity} for a reactive web application if this
|
||||
* annotation has not been added by the user. This configuration also backs off if a bean
|
||||
* of type {@link WebFilterChainProxy} has been configured in any other way.
|
||||
* annotation has not been added by the user. It delegates
|
||||
* to Spring Security's content-negotiation mechanism for authentication. This configuration also
|
||||
* backs off if a bean of type {@link WebFilterChainProxy} has been configured in any other way.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
|
||||
@@ -27,26 +27,20 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security. Provides an
|
||||
* {@link InMemoryUserDetailsManager} with one user (named "user") whose password is
|
||||
* random and printed on the console at INFO level during startup. In a webapp, this
|
||||
* configuration also secures all web endpoints (including static resources).
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ AuthenticationManager.class, EnableWebSecurity.class })
|
||||
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
|
||||
AuthenticationManagerConfiguration.class, SecurityDataConfiguration.class })
|
||||
SecurityDataConfiguration.class })
|
||||
public class SecurityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.security.servlet;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
@@ -35,6 +36,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
|
||||
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
public class SpringBootWebSecurityConfiguration {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
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.security.SecurityProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -47,10 +48,11 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(AuthenticationManager.class)
|
||||
@ConditionalOnBean(ObjectPostProcessor.class)
|
||||
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
|
||||
UserDetailsService.class })
|
||||
public class AuthenticationManagerConfiguration {
|
||||
public class UserDetailsServiceAutoConfiguration {
|
||||
|
||||
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
|
||||
|
||||
@@ -58,7 +60,7 @@ public class AuthenticationManagerConfiguration {
|
||||
.compile("^\\{.+}.*$");
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(AuthenticationManagerConfiguration.class);
|
||||
.getLog(UserDetailsServiceAutoConfiguration.class);
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
|
||||
@@ -97,8 +97,10 @@ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
|
||||
|
||||
@@ -17,25 +17,10 @@
|
||||
package org.springframework.boot.autoconfigure.security.reactive;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -49,10 +34,11 @@ public class ReactiveSecurityAutoConfigurationTests {
|
||||
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
|
||||
|
||||
@Test
|
||||
public void enablesWebFluxSecurity() {
|
||||
this.contextRunner.withUserConfiguration(TestConfig.class)
|
||||
public void importsConfigurationThatEnablesWebFluxSecurity() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveUserDetailsServiceAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
|
||||
assertThat(context).getBean(WebFluxSecurityConfiguration.class)
|
||||
@@ -61,90 +47,4 @@ public class ReactiveSecurityAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresADefaultUser() {
|
||||
this.contextRunner.withUserConfiguration(TestConfig.class)
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
ReactiveUserDetailsService userDetailsService = context
|
||||
.getBean(ReactiveUserDetailsService.class);
|
||||
assertThat(userDetailsService.findByUsername("user").block())
|
||||
.isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotConfigureDefaultUserIfUserDetailsServiceAvailable() {
|
||||
this.contextRunner.withUserConfiguration(UserConfig.class, TestConfig.class)
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
ReactiveUserDetailsService userDetailsService = context
|
||||
.getBean(ReactiveUserDetailsService.class);
|
||||
assertThat(userDetailsService.findByUsername("user").block())
|
||||
.isNull();
|
||||
assertThat(userDetailsService.findByUsername("foo").block())
|
||||
.isNotNull();
|
||||
assertThat(userDetailsService.findByUsername("admin").block())
|
||||
.isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotConfigureDefaultUserIfAuthenticationManagerAvailable() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(AuthenticationManagerConfig.class,
|
||||
TestConfig.class)
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context)
|
||||
.getBean(ReactiveUserDetailsService.class).isNull());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHandler(ApplicationContext applicationContext) {
|
||||
return WebHttpHandlerBuilder.applicationContext(applicationContext).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveWebServerFactory reactiveWebServerFactory() {
|
||||
return new MockReactiveWebServerFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserConfig {
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
UserDetails foo = User.withUsername("foo").password("foo").roles("USER")
|
||||
.build();
|
||||
UserDetails admin = User.withUsername("admin").password("admin")
|
||||
.roles("USER", "ADMIN").build();
|
||||
return new MapReactiveUserDetailsService(foo, admin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationManagerConfig {
|
||||
|
||||
@Bean
|
||||
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
|
||||
return new ReactiveAuthenticationManager() {
|
||||
@Override
|
||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,34 +17,79 @@
|
||||
package org.springframework.boot.autoconfigure.security.reactive;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveAuthenticationManagerConfiguration}.
|
||||
* Tests for {@link ReactiveUserDetailsServiceAutoConfiguration}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class ReactiveAuthenticationManagerConfigurationTests {
|
||||
public class ReactiveUserDetailsServiceAutoConfigurationTests {
|
||||
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveUserDetailsServiceAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void configuresADefaultUser() {
|
||||
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class)
|
||||
.run((context) -> {
|
||||
ReactiveUserDetailsService userDetailsService = context
|
||||
.getBean(ReactiveUserDetailsService.class);
|
||||
assertThat(userDetailsService.findByUsername("user").block())
|
||||
.isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotConfigureDefaultUserIfUserDetailsServiceAvailable() {
|
||||
this.contextRunner.withUserConfiguration(UserConfig.class, TestSecurityConfiguration.class)
|
||||
.run((context) -> {
|
||||
ReactiveUserDetailsService userDetailsService = context
|
||||
.getBean(ReactiveUserDetailsService.class);
|
||||
assertThat(userDetailsService.findByUsername("user").block())
|
||||
.isNull();
|
||||
assertThat(userDetailsService.findByUsername("foo").block())
|
||||
.isNotNull();
|
||||
assertThat(userDetailsService.findByUsername("admin").block())
|
||||
.isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotConfigureDefaultUserIfAuthenticationManagerAvailable() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(AuthenticationManagerConfig.class,
|
||||
TestSecurityConfiguration.class)
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context)
|
||||
.getBean(ReactiveUserDetailsService.class).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestSecurityConfiguration.class,
|
||||
ReactiveAuthenticationManagerConfiguration.class)
|
||||
.withUserConfiguration(TestSecurityConfiguration.class)
|
||||
.run(((context) -> {
|
||||
MapReactiveUserDetailsService userDetailsService = context
|
||||
.getBean(MapReactiveUserDetailsService.class);
|
||||
@@ -73,8 +118,7 @@ public class ReactiveAuthenticationManagerConfigurationTests {
|
||||
private void testPasswordEncoding(Class<?> configClass, String providedPassword,
|
||||
String expectedPassword) {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(configClass,
|
||||
ReactiveAuthenticationManagerConfiguration.class)
|
||||
.withUserConfiguration(configClass)
|
||||
.withPropertyValues("spring.security.user.password=" + providedPassword)
|
||||
.run(((context) -> {
|
||||
MapReactiveUserDetailsService userDetailsService = context
|
||||
@@ -92,6 +136,35 @@ public class ReactiveAuthenticationManagerConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserConfig {
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
UserDetails foo = User.withUsername("foo").password("foo").roles("USER")
|
||||
.build();
|
||||
UserDetails admin = User.withUsername("admin").password("admin")
|
||||
.roles("USER", "ADMIN").build();
|
||||
return new MapReactiveUserDetailsService(foo, admin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationManagerConfig {
|
||||
|
||||
@Bean
|
||||
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
|
||||
return new ReactiveAuthenticationManager() {
|
||||
@Override
|
||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestSecurityConfiguration.class)
|
||||
protected static class TestConfigWithPasswordEncoder {
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.reactive;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebFluxSecurityConfiguration}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class WebFluxSecurityConfigurationTests {
|
||||
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
|
||||
|
||||
@Test
|
||||
public void backsOffWhenWebFilterChainProxyBeanPresent() {
|
||||
this.contextRunner.withUserConfiguration(WebFilterChainProxyConfiguration.class, WebFluxSecurityConfiguration.class)
|
||||
.run(context -> assertThat(context).doesNotHaveBean(WebFluxSecurityConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enablesWebFluxSecurity() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveUserDetailsServiceAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
|
||||
assertThat(context).getBean(WebFluxSecurityConfiguration.class)
|
||||
.isNotNull();
|
||||
assertThat(context).getBean(WebFilterChainProxy.class).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WebFilterChainProxyConfiguration {
|
||||
|
||||
@Bean
|
||||
public WebFilterChainProxy webFilterChainProxy() {
|
||||
return mock(WebFilterChainProxy.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.servlet;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthenticationManagerConfiguration}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class AuthenticationManagerConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
||||
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class,
|
||||
AuthenticationManagerConfiguration.class).run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context
|
||||
.getBean(InMemoryUserDetailsManager.class);
|
||||
String password = userDetailsService.loadUserByUsername("user")
|
||||
.getPassword();
|
||||
assertThat(password).startsWith("{noop}");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndRawPassword() {
|
||||
testPasswordEncoding(TestSecurityConfiguration.class, "secret", "{noop}secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndEncodedPassword() {
|
||||
String password = "{bcrypt}$2a$10$sCBi9fy9814vUPf2ZRbtp.fR5/VgRk2iBFZ.ypu5IyZ28bZgxrVDa";
|
||||
testPasswordEncoding(TestSecurityConfiguration.class, password, password);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderBeanPresent() {
|
||||
testPasswordEncoding(TestConfigWithPasswordEncoder.class, "secret", "secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenClientRegistrationRepositoryBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestConfigWithClientRegistrationRepository.class,
|
||||
AuthenticationManagerConfiguration.class)
|
||||
.run(((context) -> assertThat(context)
|
||||
.doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
||||
}
|
||||
|
||||
private void testPasswordEncoding(Class<?> configClass, String providedPassword,
|
||||
String expectedPassword) {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(configClass,
|
||||
AuthenticationManagerConfiguration.class)
|
||||
.withPropertyValues("spring.security.user.password=" + providedPassword)
|
||||
.run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context
|
||||
.getBean(InMemoryUserDetailsManager.class);
|
||||
String password = userDetailsService.loadUserByUsername("user")
|
||||
.getPassword();
|
||||
assertThat(password).isEqualTo(expectedPassword);
|
||||
}));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
protected static class TestSecurityConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestSecurityConfiguration.class)
|
||||
protected static class TestConfigWithPasswordEncoder {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return mock(PasswordEncoder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestSecurityConfiguration.class)
|
||||
protected static class TestConfigWithClientRegistrationRepository {
|
||||
|
||||
@Bean
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
return mock(ClientRegistrationRepository.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.security.servlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
@@ -39,21 +38,13 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationProvider;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@@ -149,61 +140,6 @@ public class SecurityAutoConfigurationTests {
|
||||
.getOrder()).isEqualTo(12345));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultUsernamePassword() {
|
||||
this.contextRunner.run((context) -> {
|
||||
UserDetailsService manager = context.getBean(UserDetailsService.class);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.contains("Using generated security password:");
|
||||
assertThat(manager.loadUserByUsername("user")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultUserNotCreatedIfAuthenticationManagerBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestAuthenticationManagerConfiguration.class)
|
||||
.run((context) -> {
|
||||
AuthenticationManager manager = context
|
||||
.getBean(AuthenticationManager.class);
|
||||
assertThat(manager).isEqualTo(context.getBean(
|
||||
TestAuthenticationManagerConfiguration.class).authenticationManager);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.doesNotContain("Using generated security password: ");
|
||||
TestingAuthenticationToken token = new TestingAuthenticationToken(
|
||||
"foo", "bar");
|
||||
assertThat(manager.authenticate(token)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultUserNotCreatedIfUserDetailsServiceBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestUserDetailsServiceConfiguration.class)
|
||||
.run((context) -> {
|
||||
UserDetailsService userDetailsService = context
|
||||
.getBean(UserDetailsService.class);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.doesNotContain("Using default security password: ");
|
||||
assertThat(userDetailsService.loadUserByUsername("foo")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultUserNotCreatedIfAuthenticationProviderBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestAuthenticationProviderConfiguration.class)
|
||||
.run((context) -> {
|
||||
AuthenticationProvider provider = context
|
||||
.getBean(AuthenticationProvider.class);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.doesNotContain("Using default security password: ");
|
||||
TestingAuthenticationToken token = new TestingAuthenticationToken(
|
||||
"foo", "bar");
|
||||
assertThat(provider.authenticate(token)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJpaCoexistsHappily() {
|
||||
this.contextRunner
|
||||
@@ -292,42 +228,6 @@ public class SecurityAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TestAuthenticationManagerConfiguration {
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager myAuthenticationManager() {
|
||||
AuthenticationProvider authenticationProvider = new TestingAuthenticationProvider();
|
||||
this.authenticationManager = new ProviderManager(
|
||||
Collections.singletonList(authenticationProvider));
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TestUserDetailsServiceConfiguration {
|
||||
|
||||
@Bean
|
||||
public InMemoryUserDetailsManager myUserDetailsManager() {
|
||||
return new InMemoryUserDetailsManager(
|
||||
User.withUsername("foo").password("bar").roles("USER").build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TestAuthenticationProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
public AuthenticationProvider myAuthenticationProvider() {
|
||||
return new TestingAuthenticationProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class WebSecurity extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@@ -78,6 +78,7 @@ public class SecurityFilterAutoConfigurationEarlyInitializationTests {
|
||||
@ImportAutoConfiguration({ WebMvcAutoConfiguration.class,
|
||||
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
|
||||
DispatcherServletAutoConfiguration.class, SecurityAutoConfiguration.class,
|
||||
UserDetailsServiceAutoConfiguration.class,
|
||||
SecurityFilterAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class })
|
||||
static class Config {
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.servlet;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.rule.OutputCapture;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationProvider;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserDetailsServiceAutoConfiguration}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class UserDetailsServiceAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(TestSecurityConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(UserDetailsServiceAutoConfiguration.class));
|
||||
|
||||
@Rule
|
||||
public OutputCapture outputCapture = new OutputCapture();
|
||||
|
||||
@Test
|
||||
public void testDefaultUsernamePassword() {
|
||||
this.contextRunner.run((context) -> {
|
||||
UserDetailsService manager = context.getBean(UserDetailsService.class);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.contains("Using generated security password:");
|
||||
assertThat(manager.loadUserByUsername("user")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultUserNotCreatedIfAuthenticationManagerBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestAuthenticationManagerConfiguration.class)
|
||||
.run((context) -> {
|
||||
AuthenticationManager manager = context
|
||||
.getBean(AuthenticationManager.class);
|
||||
assertThat(manager).isEqualTo(context.getBean(
|
||||
TestAuthenticationManagerConfiguration.class).authenticationManager);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.doesNotContain("Using generated security password: ");
|
||||
TestingAuthenticationToken token = new TestingAuthenticationToken(
|
||||
"foo", "bar");
|
||||
assertThat(manager.authenticate(token)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultUserNotCreatedIfUserDetailsServiceBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestUserDetailsServiceConfiguration.class)
|
||||
.run((context) -> {
|
||||
UserDetailsService userDetailsService = context
|
||||
.getBean(UserDetailsService.class);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.doesNotContain("Using default security password: ");
|
||||
assertThat(userDetailsService.loadUserByUsername("foo")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultUserNotCreatedIfAuthenticationProviderBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestAuthenticationProviderConfiguration.class)
|
||||
.run((context) -> {
|
||||
AuthenticationProvider provider = context
|
||||
.getBean(AuthenticationProvider.class);
|
||||
assertThat(this.outputCapture.toString())
|
||||
.doesNotContain("Using default security password: ");
|
||||
TestingAuthenticationToken token = new TestingAuthenticationToken(
|
||||
"foo", "bar");
|
||||
assertThat(provider.authenticate(token)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() {
|
||||
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context
|
||||
.getBean(InMemoryUserDetailsManager.class);
|
||||
String password = userDetailsService.loadUserByUsername("user")
|
||||
.getPassword();
|
||||
assertThat(password).startsWith("{noop}");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndRawPassword() {
|
||||
testPasswordEncoding(TestSecurityConfiguration.class, "secret", "{noop}secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderAbsentAndEncodedPassword() {
|
||||
String password = "{bcrypt}$2a$10$sCBi9fy9814vUPf2ZRbtp.fR5/VgRk2iBFZ.ypu5IyZ28bZgxrVDa";
|
||||
testPasswordEncoding(TestSecurityConfiguration.class, password, password);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenPasswordEncoderBeanPresent() {
|
||||
testPasswordEncoding(TestConfigWithPasswordEncoder.class, "secret", "secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userDetailsServiceWhenClientRegistrationRepositoryBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestConfigWithClientRegistrationRepository.class)
|
||||
.run(((context) -> assertThat(context)
|
||||
.doesNotHaveBean(InMemoryUserDetailsManager.class)));
|
||||
}
|
||||
|
||||
private void testPasswordEncoding(Class<?> configClass, String providedPassword,
|
||||
String expectedPassword) {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(configClass)
|
||||
.withPropertyValues("spring.security.user.password=" + providedPassword)
|
||||
.run(((context) -> {
|
||||
InMemoryUserDetailsManager userDetailsService = context
|
||||
.getBean(InMemoryUserDetailsManager.class);
|
||||
String password = userDetailsService.loadUserByUsername("user")
|
||||
.getPassword();
|
||||
assertThat(password).isEqualTo(expectedPassword);
|
||||
}));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TestAuthenticationManagerConfiguration {
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager myAuthenticationManager() {
|
||||
AuthenticationProvider authenticationProvider = new TestingAuthenticationProvider();
|
||||
this.authenticationManager = new ProviderManager(
|
||||
Collections.singletonList(authenticationProvider));
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TestUserDetailsServiceConfiguration {
|
||||
|
||||
@Bean
|
||||
public InMemoryUserDetailsManager myUserDetailsManager() {
|
||||
return new InMemoryUserDetailsManager(
|
||||
User.withUsername("foo").password("bar").roles("USER").build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TestAuthenticationProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
public AuthenticationProvider myAuthenticationProvider() {
|
||||
return new TestingAuthenticationProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
protected static class TestSecurityConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestSecurityConfiguration.class)
|
||||
protected static class TestConfigWithPasswordEncoder {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return mock(PasswordEncoder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestSecurityConfiguration.class)
|
||||
protected static class TestConfigWithClientRegistrationRepository {
|
||||
|
||||
@Bean
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
return mock(ClientRegistrationRepository.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user