From 1b0cf7bd22028e5cbd07039ecb3f409d66a98c6a Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Thu, 25 Jul 2019 10:51:53 -0700 Subject: [PATCH] Default password should not be generated when using resource server See gh-17646 --- ...OAuth2ResourceServerAutoConfiguration.java | 3 +- ...OAuth2ResourceServerAutoConfiguration.java | 3 +- ...veUserDetailsServiceAutoConfiguration.java | 4 ++- .../UserDetailsServiceAutoConfiguration.java | 5 ++- ...rDetailsServiceAutoConfigurationTests.java | 29 ++++++++++++++++ ...rDetailsServiceAutoConfigurationTests.java | 34 +++++++++++++++++++ 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java index bce0c99213..7f6279dd1a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java @@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -38,7 +39,7 @@ import org.springframework.security.oauth2.server.resource.introspection.Reactiv * @since 2.1.0 */ @Configuration(proxyBeanMethods = false) -@AutoConfigureBefore(ReactiveSecurityAutoConfiguration.class) +@AutoConfigureBefore({ ReactiveSecurityAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class }) @EnableConfigurationProperties(OAuth2ResourceServerProperties.class) @ConditionalOnClass({ EnableWebFluxSecurity.class }) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfiguration.java index d5e05b4890..c4cd3c5b82 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfiguration.java @@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -36,7 +37,7 @@ import org.springframework.security.oauth2.server.resource.introspection.OAuth2T * @since 2.1.0 */ @Configuration(proxyBeanMethods = false) -@AutoConfigureBefore(SecurityAutoConfiguration.class) +@AutoConfigureBefore({ SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class }) @EnableConfigurationProperties(OAuth2ResourceServerProperties.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java index b562b50554..d0b1ed19e2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.java @@ -48,7 +48,9 @@ import org.springframework.util.StringUtils; */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ ReactiveAuthenticationManager.class }) -@ConditionalOnMissingBean({ ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class }) +@ConditionalOnMissingBean(value = { ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class }, type = { + "org.springframework.security.oauth2.jwt.ReactiveJwtDecoder", + "org.springframework.security.oauth2.server.resource.introspection.ReactiveOAuth2TokenIntrospectionClient" }) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) public class ReactiveUserDetailsServiceAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java index aaa078cd2a..5ba125a2da 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.java @@ -55,7 +55,10 @@ import org.springframework.util.StringUtils; @Configuration(proxyBeanMethods = false) @ConditionalOnClass(AuthenticationManager.class) @ConditionalOnBean(ObjectPostProcessor.class) -@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class }) +@ConditionalOnMissingBean( + value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class }, + type = { "org.springframework.security.oauth2.jwt.JwtDecoder", + "org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient" }) public class UserDetailsServiceAutoConfiguration { private static final String NOOP_PASSWORD_PREFIX = "{noop}"; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java index 3974462c32..c4bd28bf2a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfigurationTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -34,6 +35,8 @@ 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 org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; +import org.springframework.security.oauth2.server.resource.introspection.ReactiveOAuth2TokenIntrospectionClient; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -73,6 +76,32 @@ class ReactiveUserDetailsServiceAutoConfigurationTests { .run((context) -> assertThat(context).getBean(ReactiveUserDetailsService.class).isNull()); } + @Test + void doesNotConfigureDefaultUserIfResourceServerWithJWTIsUsed() { + this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class) + .withConfiguration(AutoConfigurations.of(ReactiveOAuth2ResourceServerAutoConfiguration.class)) + .withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys") + .run((context) -> { + assertThat(context).hasSingleBean(ReactiveJwtDecoder.class); + assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class); + }); + } + + @Test + void doesNotConfigureDefaultUserIfResourceServerWithOpaqueIsUsed() { + this.contextRunner.withConfiguration(AutoConfigurations.of(ReactiveOAuth2ResourceServerAutoConfiguration.class)) + .withUserConfiguration(TestSecurityConfiguration.class) + .withPropertyValues( + "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", + "spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id", + "spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret") + .run((context) -> { + assertThat(context).hasSingleBean(ReactiveOAuth2TokenIntrospectionClient.class); + assertThat(context).doesNotHaveBean(ReactiveUserDetailsService.class); + }); + } + @Test void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() { this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java index 78c8eb85de..1eba2ba8fe 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfigurationTests.java @@ -23,8 +23,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.context.annotation.Bean; @@ -42,6 +44,8 @@ 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.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import static org.assertj.core.api.Assertions.assertThat; @@ -99,6 +103,36 @@ class UserDetailsServiceAutoConfigurationTests { }); } + @Test + void defaultUserNotCreatedIfResourceServerWithOpaqueIsUsed() { + WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner(); + webApplicationContextRunner + .withConfiguration(AutoConfigurations.of(OAuth2ResourceServerAutoConfiguration.class)) + .withUserConfiguration(TestSecurityConfiguration.class) + .withPropertyValues( + "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", + "spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id", + "spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret") + .run((context) -> { + assertThat(context).hasSingleBean(OAuth2TokenIntrospectionClient.class); + assertThat(context).doesNotHaveBean(UserDetailsService.class); + }); + } + + @Test + void defaultUserNotCreatedIfResourceServerWithJWTIsUsed() { + WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner(); + webApplicationContextRunner + .withConfiguration(AutoConfigurations.of(OAuth2ResourceServerAutoConfiguration.class)) + .withUserConfiguration(TestSecurityConfiguration.class) + .withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys") + .run((context) -> { + assertThat(context).hasSingleBean(JwtDecoder.class); + assertThat(context).doesNotHaveBean(UserDetailsService.class); + }); + } + @Test void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() { this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class).run(((context) -> {