Enable customization of JWK Set URI decoder builders
Closes gh-20750
This commit is contained in:
parent
45068c777f
commit
a03fe8befc
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2012-2023 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.security.oauth2.jwt.NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
|
||||
/**
|
||||
* Callback interface for the customization of the
|
||||
* {@link JwkSetUriReactiveJwtDecoderBuilder} used to create the auto-configured
|
||||
* {@link ReactiveJwtDecoder} for a JWK set URI that has been configured directly or
|
||||
* obtained through an issuer URI.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JwkSetUriReactiveJwtDecoderBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the given {@code builder}.
|
||||
* @param builder the {@code builder} to customize
|
||||
*/
|
||||
void customize(JwkSetUriReactiveJwtDecoderBuilder builder);
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
@ -45,8 +46,8 @@ import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
|||
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
|
||||
import org.springframework.security.oauth2.jwt.JwtValidators;
|
||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
|
||||
import org.springframework.security.oauth2.jwt.SupplierReactiveJwtDecoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
@ -77,11 +78,12 @@ class ReactiveOAuth2ResourceServerJwkConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
|
||||
ReactiveJwtDecoder jwtDecoder() {
|
||||
NimbusReactiveJwtDecoder nimbusReactiveJwtDecoder = NimbusReactiveJwtDecoder
|
||||
ReactiveJwtDecoder jwtDecoder(ObjectProvider<JwkSetUriReactiveJwtDecoderBuilderCustomizer> customizers) {
|
||||
JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder
|
||||
.withJwkSetUri(this.properties.getJwkSetUri())
|
||||
.jwsAlgorithms(this::jwsAlgorithms)
|
||||
.build();
|
||||
.jwsAlgorithms(this::jwsAlgorithms);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
NimbusReactiveJwtDecoder nimbusReactiveJwtDecoder = builder.build();
|
||||
String issuerUri = this.properties.getIssuerUri();
|
||||
Supplier<OAuth2TokenValidator<Jwt>> defaultValidator = (issuerUri != null)
|
||||
? () -> JwtValidators.createDefaultWithIssuer(issuerUri) : JwtValidators::createDefault;
|
||||
|
@ -138,10 +140,13 @@ class ReactiveOAuth2ResourceServerJwkConfiguration {
|
|||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
SupplierReactiveJwtDecoder jwtDecoderByIssuerUri() {
|
||||
SupplierReactiveJwtDecoder jwtDecoderByIssuerUri(
|
||||
ObjectProvider<JwkSetUriReactiveJwtDecoderBuilderCustomizer> customizers) {
|
||||
return new SupplierReactiveJwtDecoder(() -> {
|
||||
NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder) ReactiveJwtDecoders
|
||||
.fromIssuerLocation(this.properties.getIssuerUri());
|
||||
JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder
|
||||
.withIssuerLocation(this.properties.getIssuerUri());
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
NimbusReactiveJwtDecoder jwtDecoder = builder.build();
|
||||
jwtDecoder.setJwtValidator(
|
||||
getValidators(() -> JwtValidators.createDefaultWithIssuer(this.properties.getIssuerUri())));
|
||||
return jwtDecoder;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2012-2023 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.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder;
|
||||
|
||||
/**
|
||||
* Callback interface for the customization of the {@link JwkSetUriJwtDecoderBuilder} used
|
||||
* to create the auto-configured {@link JwtDecoder} for a JWK set URI that has been
|
||||
* configured directly or obtained through an issuer URI.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JwkSetUriJwtDecoderBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the given {@code builder}.
|
||||
* @param builder the {@code builder} to customize
|
||||
*/
|
||||
void customize(JwkSetUriJwtDecoderBuilder builder);
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
@ -44,9 +45,9 @@ import org.springframework.security.oauth2.jwt.Jwt;
|
|||
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
|
||||
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.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder;
|
||||
import org.springframework.security.oauth2.jwt.SupplierJwtDecoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
@ -78,10 +79,11 @@ class OAuth2ResourceServerJwtConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
|
||||
JwtDecoder jwtDecoderByJwkKeySetUri() {
|
||||
NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
|
||||
.jwsAlgorithms(this::jwsAlgorithms)
|
||||
.build();
|
||||
JwtDecoder jwtDecoderByJwkKeySetUri(ObjectProvider<JwkSetUriJwtDecoderBuilderCustomizer> customizers) {
|
||||
JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
|
||||
.jwsAlgorithms(this::jwsAlgorithms);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
NimbusJwtDecoder nimbusJwtDecoder = builder.build();
|
||||
String issuerUri = this.properties.getIssuerUri();
|
||||
Supplier<OAuth2TokenValidator<Jwt>> defaultValidator = (issuerUri != null)
|
||||
? () -> JwtValidators.createDefaultWithIssuer(issuerUri) : JwtValidators::createDefault;
|
||||
|
@ -138,10 +140,12 @@ class OAuth2ResourceServerJwtConfiguration {
|
|||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
SupplierJwtDecoder jwtDecoderByIssuerUri() {
|
||||
SupplierJwtDecoder jwtDecoderByIssuerUri(ObjectProvider<JwkSetUriJwtDecoderBuilderCustomizer> customizers) {
|
||||
return new SupplierJwtDecoder(() -> {
|
||||
String issuerUri = this.properties.getIssuerUri();
|
||||
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri);
|
||||
JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withIssuerLocation(issuerUri);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
NimbusJwtDecoder jwtDecoder = builder.build();
|
||||
jwtDecoder.setJwtValidator(getValidators(() -> JwtValidators.createDefaultWithIssuer(issuerUri)));
|
||||
return jwtDecoder;
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@ import okhttp3.mockwebserver.MockWebServer;
|
|||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
|
@ -43,6 +44,7 @@ import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplic
|
|||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -72,6 +74,8 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
|
||||
|
@ -92,7 +96,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
|
||||
private MockWebServer server;
|
||||
|
||||
private static final Duration TIMEOUT = Duration.ofSeconds(5);
|
||||
private static final Duration TIMEOUT = Duration.ofSeconds(5000000);
|
||||
|
||||
private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\","
|
||||
+ "\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGm"
|
||||
|
@ -127,9 +131,21 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
assertThat(nimbusReactiveJwtDecoder).extracting("jwtProcessor.arg$1.signatureAlgorithms")
|
||||
.asInstanceOf(InstanceOfAssertFactories.collection(SignatureAlgorithm.class))
|
||||
.containsExactlyInAnyOrder(SignatureAlgorithm.RS512);
|
||||
assertJwkSetUriReactiveJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertJwkSetUriReactiveJwtDecoderBuilderCustomization(
|
||||
AssertableReactiveWebApplicationContext context) {
|
||||
JwkSetUriReactiveJwtDecoderBuilderCustomizer customizer = context.getBean("decoderBuilderCustomizer",
|
||||
JwkSetUriReactiveJwtDecoderBuilderCustomizer.class);
|
||||
JwkSetUriReactiveJwtDecoderBuilderCustomizer anotherCustomizer = context
|
||||
.getBean("anotherDecoderBuilderCustomizer", JwkSetUriReactiveJwtDecoderBuilderCustomizer.class);
|
||||
InOrder inOrder = inOrder(customizer, anotherCustomizer);
|
||||
inOrder.verify(customizer).customize(any());
|
||||
inOrder.verify(anotherCustomizer).customize(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationUsingJwkSetUriShouldConfigureResourceServerUsingMultipleJwsAlgorithms() {
|
||||
this.contextRunner
|
||||
|
@ -141,6 +157,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
.asInstanceOf(InstanceOfAssertFactories.collection(SignatureAlgorithm.class))
|
||||
.containsExactlyInAnyOrder(SignatureAlgorithm.RS256, SignatureAlgorithm.RS384,
|
||||
SignatureAlgorithm.RS512);
|
||||
assertJwkSetUriReactiveJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -172,7 +189,6 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws IOException {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
@ -187,18 +203,32 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
assertThat(context).hasSingleBean(SupplierReactiveJwtDecoder.class);
|
||||
assertFilterConfiguredWithJwtAuthenticationManager(context);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context
|
||||
.getBean(SupplierReactiveJwtDecoder.class);
|
||||
Mono<ReactiveJwtDecoder> reactiveJwtDecoderSupplier = (Mono<ReactiveJwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierReactiveJwtDecoder, "jwtDecoderMono");
|
||||
reactiveJwtDecoderSupplier.block(TIMEOUT);
|
||||
// Trigger calls to the issuer by decoding a token
|
||||
decodeJwt(context);
|
||||
assertJwkSetUriReactiveJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isOne();
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void decodeJwt(AssertableReactiveWebApplicationContext context) {
|
||||
SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context.getBean(SupplierReactiveJwtDecoder.class);
|
||||
Mono<ReactiveJwtDecoder> reactiveJwtDecoderSupplier = (Mono<ReactiveJwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierReactiveJwtDecoder, "jwtDecoderMono");
|
||||
try {
|
||||
reactiveJwtDecoderSupplier.flatMap((decoder) -> decoder.decode("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."
|
||||
+ "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0."
|
||||
+ "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"))
|
||||
.block(TIMEOUT);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// This fails, but it's enough to check that the expected HTTP calls
|
||||
// are made
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
@ -212,18 +242,15 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
assertThat(context).hasSingleBean(SupplierReactiveJwtDecoder.class);
|
||||
assertFilterConfiguredWithJwtAuthenticationManager(context);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context
|
||||
.getBean(SupplierReactiveJwtDecoder.class);
|
||||
Mono<ReactiveJwtDecoder> reactiveJwtDecoderSupplier = (Mono<ReactiveJwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierReactiveJwtDecoder, "jwtDecoderMono");
|
||||
reactiveJwtDecoderSupplier.block(TIMEOUT);
|
||||
// Trigger calls to the issuer by decoding a token
|
||||
decodeJwt(context);
|
||||
// assertJwkSetUriReactiveJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(2);
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
@ -237,14 +264,12 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
assertThat(context).hasSingleBean(SupplierReactiveJwtDecoder.class);
|
||||
assertFilterConfiguredWithJwtAuthenticationManager(context);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context
|
||||
.getBean(SupplierReactiveJwtDecoder.class);
|
||||
Mono<ReactiveJwtDecoder> reactiveJwtDecoderSupplier = (Mono<ReactiveJwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierReactiveJwtDecoder, "jwtDecoderMono");
|
||||
reactiveJwtDecoderSupplier.block(TIMEOUT);
|
||||
// Trigger calls to the issuer by decoding a token
|
||||
decodeJwt(context);
|
||||
assertJwkSetUriReactiveJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -666,6 +691,18 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
return mock(MapReactiveUserDetailsService.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
JwkSetUriReactiveJwtDecoderBuilderCustomizer decoderBuilderCustomizer() {
|
||||
return mock(JwkSetUriReactiveJwtDecoderBuilderCustomizer.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(2)
|
||||
JwkSetUriReactiveJwtDecoderBuilderCustomizer anotherDecoderBuilderCustomizer() {
|
||||
return mock(JwkSetUriReactiveJwtDecoderBuilderCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
|
@ -35,6 +35,7 @@ import okhttp3.mockwebserver.MockWebServer;
|
|||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
||||
|
@ -43,6 +44,7 @@ import org.springframework.boot.test.context.assertj.AssertableWebApplicationCon
|
|||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -67,6 +69,8 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
|
@ -105,9 +109,20 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JwtDecoder.class);
|
||||
assertThat(getBearerTokenFilter(context)).isNotNull();
|
||||
assertJwkSetUriJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertJwkSetUriJwtDecoderBuilderCustomization(AssertableWebApplicationContext context) {
|
||||
JwkSetUriJwtDecoderBuilderCustomizer customizer = context.getBean("decoderBuilderCustomizer",
|
||||
JwkSetUriJwtDecoderBuilderCustomizer.class);
|
||||
JwkSetUriJwtDecoderBuilderCustomizer anotherCustomizer = context.getBean("anotherDecoderBuilderCustomizer",
|
||||
JwkSetUriJwtDecoderBuilderCustomizer.class);
|
||||
InOrder inOrder = inOrder(customizer, anotherCustomizer);
|
||||
inOrder.verify(customizer).customize(any());
|
||||
inOrder.verify(anotherCustomizer).customize(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationShouldMatchDefaultJwsAlgorithm() {
|
||||
this.contextRunner
|
||||
|
@ -194,6 +209,7 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
Supplier<JwtDecoder> jwtDecoderSupplier = (Supplier<JwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierJwtDecoderBean, "delegate");
|
||||
jwtDecoderSupplier.get();
|
||||
assertJwkSetUriJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(2);
|
||||
|
@ -218,6 +234,7 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
Supplier<JwtDecoder> jwtDecoderSupplier = (Supplier<JwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierJwtDecoderBean, "delegate");
|
||||
jwtDecoderSupplier.get();
|
||||
assertJwkSetUriJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
|
@ -243,6 +260,7 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
Supplier<JwtDecoder> jwtDecoderSupplier = (Supplier<JwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierJwtDecoderBean, "delegate");
|
||||
jwtDecoderSupplier.get();
|
||||
assertJwkSetUriJwtDecoderBuilderCustomization(context);
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(4);
|
||||
|
@ -678,6 +696,18 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
@EnableWebSecurity
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
JwkSetUriJwtDecoderBuilderCustomizer decoderBuilderCustomizer() {
|
||||
return mock(JwkSetUriJwtDecoderBuilderCustomizer.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(2)
|
||||
JwkSetUriJwtDecoderBuilderCustomizer anotherDecoderBuilderCustomizer() {
|
||||
return mock(JwkSetUriJwtDecoderBuilderCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
Loading…
Reference in New Issue