diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java index fe6eff975d7..2c9956710a3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -19,8 +19,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import javax.annotation.PostConstruct; - import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; import org.springframework.core.io.Resource; @@ -49,26 +47,6 @@ public class OAuth2ResourceServerProperties { return this.opaqueToken; } - @PostConstruct - public void validate() { - if (this.getOpaquetoken().getIntrospectionUri() != null) { - if (this.getJwt().getJwkSetUri() != null) { - handleError("jwt.jwk-set-uri"); - } - if (this.getJwt().getIssuerUri() != null) { - handleError("jwt.issuer-uri"); - } - if (this.getJwt().getPublicKeyLocation() != null) { - handleError("jwt.public-key-location"); - } - } - } - - private void handleError(String property) { - throw new IllegalStateException( - "Only one of " + property + " and opaquetoken.introspection-uri should be configured."); - } - public static class Jwt { /** 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 74d8e0b5275..e214b679867 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -26,9 +26,6 @@ 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.oauth2.jwt.ReactiveJwtDecoder; -import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; -import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector; /** * {@link EnableAutoConfiguration Auto-configuration} for Reactive OAuth2 resource server @@ -42,22 +39,8 @@ import org.springframework.security.oauth2.server.resource.introspection.Reactiv @EnableConfigurationProperties(OAuth2ResourceServerProperties.class) @ConditionalOnClass({ EnableWebFluxSecurity.class }) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) +@Import({ ReactiveOAuth2ResourceServerConfiguration.JwtConfiguration.class, + ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenConfiguration.class }) public class ReactiveOAuth2ResourceServerAutoConfiguration { - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class }) - @Import({ ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class, - ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class }) - static class JwtConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class }) - @Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class, - ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class }) - static class OpaqueTokenConfiguration { - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java new file mode 100644 index 00000000000..2911e9afd5e --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.oauth2.resource.reactive; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector; + +/** + * Configuration classes for OAuth2 Resource Server These should be {@code @Import} in a + * regular auto-configuration class to guarantee their order of execution. + * + * @author Madhura Bhave + */ +class ReactiveOAuth2ResourceServerConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class }) + @Import({ ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class, + ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class }) + static class JwtConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class }) + @Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class, + ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class }) + static class OpaqueTokenConfiguration { + + } + +} 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 eb36410c0db..1ceb7308a6f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -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; @@ -25,10 +24,6 @@ import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServic import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; /** * {@link EnableAutoConfiguration Auto-configuration} for OAuth2 resource server support. @@ -40,23 +35,8 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT @AutoConfigureBefore({ SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class }) @EnableConfigurationProperties(OAuth2ResourceServerProperties.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) - +@Import({ Oauth2ResourceServerConfiguration.JwtConfiguration.class, + Oauth2ResourceServerConfiguration.OpaqueTokenConfiguration.class }) public class OAuth2ResourceServerAutoConfiguration { - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({ JwtAuthenticationToken.class, JwtDecoder.class }) - @Import({ OAuth2ResourceServerJwtConfiguration.JwtDecoderConfiguration.class, - OAuth2ResourceServerJwtConfiguration.OAuth2WebSecurityConfigurerAdapter.class }) - static class JwtConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({ BearerTokenAuthenticationToken.class, OpaqueTokenIntrospector.class }) - @Import({ OAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class, - OAuth2ResourceServerOpaqueTokenConfiguration.OAuth2WebSecurityConfigurerAdapter.class }) - static class OpaqueTokenConfiguration { - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/Oauth2ResourceServerConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/Oauth2ResourceServerConfiguration.java new file mode 100644 index 00000000000..e5bec8fb183 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/Oauth2ResourceServerConfiguration.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.oauth2.resource.servlet; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; + +/** + * Configuration classes for OAuth2 Resource Server + * These should be {@code @Import} in a regular auto-configuration class to guarantee + * their order of execution. + * + * @author Madhura Bhave + */ +class Oauth2ResourceServerConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ JwtAuthenticationToken.class, JwtDecoder.class }) + @Import({ OAuth2ResourceServerJwtConfiguration.JwtDecoderConfiguration.class, + OAuth2ResourceServerJwtConfiguration.OAuth2WebSecurityConfigurerAdapter.class }) + static class JwtConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ BearerTokenAuthenticationToken.class, OpaqueTokenIntrospector.class }) + @Import({ OAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class, + OAuth2ResourceServerOpaqueTokenConfiguration.OAuth2WebSecurityConfigurerAdapter.class }) + static class OpaqueTokenConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java index 7059be76155..3a6bf77c309 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java @@ -288,6 +288,20 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { }); } + @Test + void autoConfigurationWhenJwkSetUriAndIntrospectionUriAvailable() { + this.contextRunner + .withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com", + "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(ReactiveOpaqueTokenIntrospector.class); + assertThat(context).hasSingleBean(ReactiveJwtDecoder.class); + assertFilterConfiguredWithJwtAuthenticationManager(context); + }); + } + @Test void opaqueTokenIntrospectorIsConditionalOnMissingBean() { this.contextRunner @@ -320,36 +334,6 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(ReactiveOpaqueTokenIntrospector.class)); } - @Test - void autoConfigurationWhenBothJwkSetUriAndTokenIntrospectionUriSetShouldFail() { - this.contextRunner - .withPropertyValues( - "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", - "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com") - .run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining( - "Only one of jwt.jwk-set-uri and opaquetoken.introspection-uri should be configured.")); - } - - @Test - void autoConfigurationWhenBothJwtIssuerUriAndTokenIntrospectionUriSetShouldFail() { - this.contextRunner - .withPropertyValues( - "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", - "spring.security.oauth2.resourceserver.jwt.issuer-uri=https://jwk-oidc-issuer-location.com") - .run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining( - "Only one of jwt.issuer-uri and opaquetoken.introspection-uri should be configured.")); - } - - @Test - void autoConfigurationWhenBothJwtKeyLocationAndTokenIntrospectionUriSetShouldFail() { - this.contextRunner - .withPropertyValues( - "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", - "spring.security.oauth2.resourceserver.jwt.public-key-location=classpath:public-key-location") - .run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining( - "Only one of jwt.public-key-location and opaquetoken.introspection-uri should be configured.")); - } - @SuppressWarnings("unchecked") @Test void autoConfigurationShouldConfigureResourceServerUsingJwkSetUriAndIssuerUri() throws Exception { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java index 66251e6400f..14b8fc85abf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java @@ -48,6 +48,7 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtIssuerValidator; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter; @@ -273,6 +274,22 @@ class OAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); } + @Test + void autoConfigurationWhenJwkSetUriAndIntrospectionUriAvailable() { + this.contextRunner + .withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com", + "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(OpaqueTokenIntrospector.class); + assertThat(context).hasSingleBean(JwtDecoder.class); + assertThat(getBearerTokenFilter(context)) + .extracting("authenticationManagerResolver.arg$1.providers").asList() + .hasAtLeastOneElementOfType(JwtAuthenticationProvider.class); + }); + } + @Test void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionClient() { this.contextRunner @@ -305,36 +322,6 @@ class OAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(OpaqueTokenIntrospector.class)); } - @Test - void autoConfigurationWhenBothJwkSetUriAndTokenIntrospectionUriSetShouldFail() { - this.contextRunner - .withPropertyValues( - "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", - "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com") - .run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining( - "Only one of jwt.jwk-set-uri and opaquetoken.introspection-uri should be configured.")); - } - - @Test - void autoConfigurationWhenBothJwtIssuerUriAndTokenIntrospectionUriSetShouldFail() { - this.contextRunner - .withPropertyValues( - "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", - "spring.security.oauth2.resourceserver.jwt.issuer-uri=https://jwk-oidc-issuer-location.com") - .run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining( - "Only one of jwt.issuer-uri and opaquetoken.introspection-uri should be configured.")); - } - - @Test - void autoConfigurationWhenBothJwtKeyLocationAndTokenIntrospectionUriSetShouldFail() { - this.contextRunner - .withPropertyValues( - "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com", - "spring.security.oauth2.resourceserver.jwt.public-key-location=classpath:public-key-location") - .run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining( - "Only one of jwt.public-key-location and opaquetoken.introspection-uri should be configured.")); - } - @SuppressWarnings("unchecked") @Test void autoConfigurationShouldConfigureResourceServerUsingJwkSetUriAndIssuerUri() throws Exception {