From 3233341d458b485da74df854d0ebd05c54794d54 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 14 Apr 2025 11:40:53 +0100 Subject: [PATCH] Improve class conditions on auth server JWT auto-config Prior to this change, introspection of the auto-configuration could fail due to insufficient protection against missing classes. This commit introduces an extra class-level check for Nimbus's JWKSource which ensures that the auto-configuration backs off if nimbus-jose-jwt has been excluded. It also introduces an inner-class for the case where spring-security-oauth2-jose is not on the classpath. This ensures that the method defining the jwtDecoder bean does not cause an introspection failure when JwtDecoder is missing. Closes gh-45177 --- ...thorizationServerJwtAutoConfiguration.java | 24 ++++++++++++------- ...zationServerJwtAutoConfigurationTests.java | 13 +++++++--- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfiguration.java index e3991546189..fb8310006d1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; @@ -49,17 +50,10 @@ import org.springframework.security.oauth2.server.authorization.config.annotatio * @since 3.1.0 */ @AutoConfiguration(after = UserDetailsServiceAutoConfiguration.class) -@ConditionalOnClass(OAuth2Authorization.class) +@ConditionalOnClass({ OAuth2Authorization.class, JWKSource.class }) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class OAuth2AuthorizationServerJwtAutoConfiguration { - @Bean - @ConditionalOnClass(JwtDecoder.class) - @ConditionalOnMissingBean - JwtDecoder jwtDecoder(JWKSource jwkSource) { - return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource); - } - @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @ConditionalOnMissingBean @@ -92,4 +86,16 @@ public class OAuth2AuthorizationServerJwtAutoConfiguration { return keyPair; } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(JwtDecoder.class) + static class JwtDecoderConfiguration { + + @Bean + @ConditionalOnMissingBean + JwtDecoder jwtDecoder(JWKSource jwkSource) { + return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfigurationTests.java index feecee85707..f340bd9839d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/server/servlet/OAuth2AuthorizationServerJwtAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -43,15 +43,22 @@ class OAuth2AuthorizationServerJwtAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(OAuth2AuthorizationServerJwtAutoConfiguration.class)); @Test - void autoConfigurationConditionalOnClassOauth2Authorization() { + void autoConfigurationConditionalOnClassOAuth2Authorization() { this.contextRunner.withClassLoader(new FilteredClassLoader(OAuth2Authorization.class)) .run((context) -> assertThat(context).doesNotHaveBean(OAuth2AuthorizationServerJwtAutoConfiguration.class)); } + @Test + void autoConfigurationConditionalOnClassJWKSource() { + this.contextRunner.withClassLoader(new FilteredClassLoader(JWKSource.class)) + .run((context) -> assertThat(context).doesNotHaveBean(OAuth2AuthorizationServerJwtAutoConfiguration.class)); + } + @Test void jwtDecoderConditionalOnClassJwtDecoder() { this.contextRunner.withClassLoader(new FilteredClassLoader(JwtDecoder.class)) - .run((context) -> assertThat(context).doesNotHaveBean("jwtDecoder")); + .run((context) -> assertThat(context).hasSingleBean(OAuth2AuthorizationServerJwtAutoConfiguration.class) + .doesNotHaveBean("jwtDecoder")); } @Test