Merge pull request #28169 from Pooja199
* pr/28169: Auto-configure JwtSupplierDecoder to defer OIDC lookup Closes gh-28169
This commit is contained in:
commit
e571894903
|
|
@ -37,6 +37,7 @@ 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;
|
||||
import org.springframework.security.oauth2.jwt.SupplierReactiveJwtDecoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
/**
|
||||
|
|
@ -91,8 +92,9 @@ class ReactiveOAuth2ResourceServerJwkConfiguration {
|
|||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
|
||||
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
|
||||
SupplierReactiveJwtDecoder jwtDecoderByIssuerUri() {
|
||||
return new SupplierReactiveJwtDecoder(
|
||||
() -> ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ 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.SupplierJwtDecoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
|
|
@ -91,8 +92,8 @@ class OAuth2ResourceServerJwtConfiguration {
|
|||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
JwtDecoder jwtDecoderByIssuerUri() {
|
||||
return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
|
||||
SupplierJwtDecoder jwtDecoderByIssuerUri() {
|
||||
return new SupplierJwtDecoder(() -> JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import okhttp3.mockwebserver.MockResponse;
|
|||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
|
|
@ -52,6 +53,7 @@ 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.jwt.SupplierReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenReactiveAuthenticationManager;
|
||||
|
|
@ -129,6 +131,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws IOException {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
|
@ -138,15 +141,21 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
setupMockResponse(cleanIssuerPath);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
|
||||
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
|
||||
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");
|
||||
ReactiveJwtDecoder reactiveJwtDecoder = reactiveJwtDecoderSupplier.block();
|
||||
});
|
||||
// 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(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
|
@ -155,15 +164,21 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
setupMockResponsesWithErrors(cleanIssuerPath, 1);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
|
||||
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
|
||||
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");
|
||||
ReactiveJwtDecoder reactiveJwtDecoder = reactiveJwtDecoderSupplier.block();
|
||||
});
|
||||
// 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(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
|
@ -172,12 +187,17 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
setupMockResponsesWithErrors(cleanIssuerPath, 2);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
|
||||
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
|
||||
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");
|
||||
ReactiveJwtDecoder reactiveJwtDecoder = reactiveJwtDecoderSupplier.block();
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(4);
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -228,7 +248,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
+ this.server.getPort(),
|
||||
"spring.security.oauth2.resourceserver.jwt.public-key-location=classpath:public-key-location")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
|
||||
assertThat(context).hasSingleBean(SupplierReactiveJwtDecoder.class);
|
||||
assertFilterConfiguredWithJwtAuthenticationManager(context);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ 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.SupplierJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
|
|
@ -127,6 +129,7 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
|
@ -136,14 +139,19 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
setupMockResponse(cleanIssuerPath);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JwtDecoder.class);
|
||||
assertThat(context).hasSingleBean(SupplierJwtDecoder.class);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class);
|
||||
Supplier<JwtDecoder> jwtDecoderSupplier = (Supplier<JwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierJwtDecoderBean, "jwtDecoderSupplier");
|
||||
JwtDecoder jwtDecoder = jwtDecoderSupplier.get();
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
|
@ -153,14 +161,19 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
setupMockResponsesWithErrors(cleanIssuerPath, 1);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JwtDecoder.class);
|
||||
assertThat(context).hasSingleBean(SupplierJwtDecoder.class);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class);
|
||||
Supplier<JwtDecoder> jwtDecoderSupplier = (Supplier<JwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierJwtDecoderBean, "jwtDecoderSupplier");
|
||||
JwtDecoder jwtDecoder = jwtDecoderSupplier.get();
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
|
|
@ -168,10 +181,15 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
String issuer = this.server.url(path).toString();
|
||||
String cleanIssuerPath = cleanIssuerPath(issuer);
|
||||
setupMockResponsesWithErrors(cleanIssuerPath, 2);
|
||||
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JwtDecoder.class);
|
||||
assertThat(context).hasSingleBean(SupplierJwtDecoder.class);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class);
|
||||
Supplier<JwtDecoder> jwtDecoderSupplier = (Supplier<JwtDecoder>) ReflectionTestUtils
|
||||
.getField(supplierJwtDecoderBean, "jwtDecoderSupplier");
|
||||
JwtDecoder jwtDecoder = jwtDecoderSupplier.get();
|
||||
});
|
||||
// The last request is to the JWK Set endpoint to look up the algorithm
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(4);
|
||||
|
|
|
|||
|
|
@ -1693,7 +1693,7 @@ bom {
|
|||
]
|
||||
}
|
||||
}
|
||||
library("Spring Security", "5.6.0-M3") {
|
||||
library("Spring Security", "5.6.0-SNAPSHOT") {
|
||||
group("org.springframework.security") {
|
||||
imports = [
|
||||
"spring-security-bom"
|
||||
|
|
|
|||
Loading…
Reference in New Issue