From b3d189cf8741b8e6af4daf56505d1b4f489d4231 Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Fri, 23 Aug 2019 18:34:52 -0700 Subject: [PATCH 1/2] Configure Issuer Validator for Resource Server See gh-17952 --- ...eOAuth2ResourceServerJwkConfiguration.java | 9 +++++- .../OAuth2ResourceServerJwtConfiguration.java | 9 +++++- ...2ResourceServerAutoConfigurationTests.java | 29 ++++++++++++++++++ ...2ResourceServerAutoConfigurationTests.java | 30 +++++++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java index e0c1a23bb7..0a6ff66aca 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2Res import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders; @@ -45,7 +46,13 @@ class ReactiveOAuth2ResourceServerJwkConfiguration { @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri") @ConditionalOnMissingBean public ReactiveJwtDecoder jwtDecoder() { - return new NimbusReactiveJwtDecoder(this.properties.getJwt().getJwkSetUri()); + NimbusReactiveJwtDecoder nimbusReactiveJwtDecoder = new NimbusReactiveJwtDecoder( + this.properties.getJwt().getJwkSetUri()); + if (this.properties.getJwt().getIssuerUri() != null) { + nimbusReactiveJwtDecoder + .setJwtValidator(JwtValidators.createDefaultWithIssuer(this.properties.getJwt().getIssuerUri())); + } + return nimbusReactiveJwtDecoder; } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java index 18adee01fa..3de824543d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtDecoders; +import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; /** @@ -46,7 +47,13 @@ class OAuth2ResourceServerJwtConfiguration { @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri") @ConditionalOnMissingBean public JwtDecoder jwtDecoderByJwkKeySetUri() { - return new NimbusJwtDecoderJwkSupport(this.properties.getJwt().getJwkSetUri()); + NimbusJwtDecoderJwkSupport nimbusJwtDecoder = new NimbusJwtDecoderJwkSupport( + this.properties.getJwt().getJwkSetUri()); + if (this.properties.getJwt().getIssuerUri() != null) { + nimbusJwtDecoder + .setJwtValidator(JwtValidators.createDefaultWithIssuer(this.properties.getJwt().getIssuerUri())); + } + return nimbusJwtDecoder; } @Bean 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 0e4c7f66e7..75189beb04 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 @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -42,6 +43,10 @@ import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; +import org.springframework.security.oauth2.core.OAuth2TokenValidator; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtIssuerValidator; import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; @@ -163,6 +168,30 @@ public class ReactiveOAuth2ResourceServerAutoConfigurationTests { }); } + @SuppressWarnings("unchecked") + @Test + public void autoConfigurationShouldConfigureResourceServerUsingJwkSetUriAndIssuerUri() throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponse(cleanIssuerPath); + this.contextRunner + .withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com", + "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + this.server.getHostName() + ":" + + this.server.getPort() + "/" + path) + .run((context) -> { + assertThat(context).hasSingleBean(ReactiveJwtDecoder.class); + ReactiveJwtDecoder reactiveJwtDecoder = context.getBean(ReactiveJwtDecoder.class); + DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils + .getField(reactiveJwtDecoder, "jwtValidator"); + Collection> tokenValidators = (Collection>) ReflectionTestUtils + .getField(jwtValidator, "tokenValidators"); + assertThat(tokenValidators.stream()).hasAtLeastOneElementOfType(JwtIssuerValidator.class); + }); + } + private void assertFilterConfiguredWithJwtAuthenticationManager(AssertableReactiveWebApplicationContext context) { MatcherSecurityWebFilterChain filterChain = (MatcherSecurityWebFilterChain) context .getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN); 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 a78a0e760a..7cfd434347 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 @@ -15,6 +15,7 @@ */ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -40,12 +41,17 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; +import org.springframework.security.oauth2.core.OAuth2TokenValidator; +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.jwt.NimbusJwtDecoderJwkSupport; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -148,6 +154,30 @@ public class OAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); } + @SuppressWarnings("unchecked") + @Test + public void autoConfigurationShouldConfigureResourceServerUsingJwkSetUriAndIssuerUri() throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponse(cleanIssuerPath); + this.contextRunner + .withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com", + "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + this.server.getHostName() + ":" + + this.server.getPort() + "/" + path) + .run((context) -> { + assertThat(context).hasSingleBean(JwtDecoder.class); + JwtDecoder jwtDecoder = context.getBean(JwtDecoder.class); + DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils + .getField(jwtDecoder, "jwtValidator"); + Collection> tokenValidators = (Collection>) ReflectionTestUtils + .getField(jwtValidator, "tokenValidators"); + assertThat(tokenValidators.stream()).hasAtLeastOneElementOfType(JwtIssuerValidator.class); + }); + } + private Filter getBearerTokenFilter(AssertableWebApplicationContext context) { FilterChainProxy filterChain = (FilterChainProxy) context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN); List filterChains = filterChain.getFilterChains(); From 28d374d84d87836ae94f7122813d313e9a372944 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Fri, 23 Aug 2019 19:31:58 -0700 Subject: [PATCH 2/2] Polish "Configure Issuer Validator for Resource Server" See gh-17952 --- .../ReactiveOAuth2ResourceServerJwkConfiguration.java | 7 ++++--- .../servlet/OAuth2ResourceServerJwtConfiguration.java | 7 ++++--- ...ReactiveOAuth2ResourceServerAutoConfigurationTests.java | 1 + .../OAuth2ResourceServerAutoConfigurationTests.java | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java index 0a6ff66aca..9d77b60661 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders; * * @author Madhura Bhave * @author Artsiom Yudovin + * @author HaiTao Zhang */ @Configuration class ReactiveOAuth2ResourceServerJwkConfiguration { @@ -48,9 +49,9 @@ class ReactiveOAuth2ResourceServerJwkConfiguration { public ReactiveJwtDecoder jwtDecoder() { NimbusReactiveJwtDecoder nimbusReactiveJwtDecoder = new NimbusReactiveJwtDecoder( this.properties.getJwt().getJwkSetUri()); - if (this.properties.getJwt().getIssuerUri() != null) { - nimbusReactiveJwtDecoder - .setJwtValidator(JwtValidators.createDefaultWithIssuer(this.properties.getJwt().getIssuerUri())); + String issuerUri = this.properties.getJwt().getIssuerUri(); + if (issuerUri != null) { + nimbusReactiveJwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri)); } return nimbusReactiveJwtDecoder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java index 3de824543d..4dcd5ab34d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java @@ -33,6 +33,7 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; * * @author Madhura Bhave * @author Artsiom Yudovin + * @author HaiTao Zhang */ @Configuration class OAuth2ResourceServerJwtConfiguration { @@ -49,9 +50,9 @@ class OAuth2ResourceServerJwtConfiguration { public JwtDecoder jwtDecoderByJwkKeySetUri() { NimbusJwtDecoderJwkSupport nimbusJwtDecoder = new NimbusJwtDecoderJwkSupport( this.properties.getJwt().getJwkSetUri()); - if (this.properties.getJwt().getIssuerUri() != null) { - nimbusJwtDecoder - .setJwtValidator(JwtValidators.createDefaultWithIssuer(this.properties.getJwt().getIssuerUri())); + String issuerUri = this.properties.getJwt().getIssuerUri(); + if (issuerUri != null) { + nimbusJwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri)); } return nimbusJwtDecoder; } 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 75189beb04..fdc97f9c47 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 @@ -65,6 +65,7 @@ import static org.mockito.Mockito.mock; * * @author Madhura Bhave * @author Artsiom Yudovin + * @author HaiTao Zhang */ public class ReactiveOAuth2ResourceServerAutoConfigurationTests { 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 7cfd434347..bf60017cf0 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 @@ -61,6 +61,7 @@ import static org.mockito.Mockito.mock; * * @author Madhura Bhave * @author Artsiom Yudovin + * @author HaiTao Zhang */ public class OAuth2ResourceServerAutoConfigurationTests {