Skip to content

Commit bc2d258

Browse files
committed
Polish "Auto-configure a JwtAuthenticationConverter"
The JwtConverter bean is only supplied, if one of the following properties is there: * spring.security.oauth2.resourceserver.jwt.authority-prefix * spring.security.oauth2.resourceserver.jwt.principal-claim-name * spring.security.oauth2.resourceserver.jwt.authorities-claim-name See spring-projectsgh-38105
1 parent 879c53f commit bc2d258

File tree

5 files changed

+99
-12
lines changed

5 files changed

+99
-12
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java

+25
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Set;
2727

2828
import org.springframework.beans.factory.ObjectProvider;
29+
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
2930
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3031
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3132
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -168,6 +169,7 @@ SupplierReactiveJwtDecoder jwtDecoderByIssuerUri(
168169

169170
@Configuration(proxyBeanMethods = false)
170171
@ConditionalOnMissingBean(ReactiveJwtAuthenticationConverter.class)
172+
@Conditional(JwtConverterPropertiesCondition.class)
171173
static class JwtConverterConfiguration {
172174

173175
private final OAuth2ResourceServerProperties.Jwt properties;
@@ -212,4 +214,27 @@ private void customDecoder(OAuth2ResourceServerSpec server, ReactiveJwtDecoder d
212214

213215
}
214216

217+
private static class JwtConverterPropertiesCondition extends AnyNestedCondition {
218+
219+
JwtConverterPropertiesCondition() {
220+
super(ConfigurationPhase.REGISTER_BEAN);
221+
}
222+
223+
@ConditionalOnProperty(prefix = "spring.security.oauth2.resourceserver.jwt", name = "authority-prefix")
224+
static class OnAuthorityPrefix {
225+
226+
}
227+
228+
@ConditionalOnProperty(prefix = "spring.security.oauth2.resourceserver.jwt", name = "principal-claim-name")
229+
static class OnPrincipalClaimName {
230+
231+
}
232+
233+
@ConditionalOnProperty(prefix = "spring.security.oauth2.resourceserver.jwt", name = "authorities-claim-name")
234+
static class OnAuthoritiesClaimName {
235+
236+
}
237+
238+
}
239+
215240
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java

+25
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Set;
2727

2828
import org.springframework.beans.factory.ObjectProvider;
29+
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
2930
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3031
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3132
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -179,6 +180,7 @@ SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
179180

180181
@Configuration(proxyBeanMethods = false)
181182
@ConditionalOnMissingBean(JwtAuthenticationConverter.class)
183+
@Conditional(JwtConverterPropertiesCondition.class)
182184
static class JwtConverterConfiguration {
183185

184186
private final OAuth2ResourceServerProperties.Jwt properties;
@@ -204,4 +206,27 @@ JwtAuthenticationConverter getJwtAuthenticationConverter() {
204206

205207
}
206208

209+
private static class JwtConverterPropertiesCondition extends AnyNestedCondition {
210+
211+
JwtConverterPropertiesCondition() {
212+
super(ConfigurationPhase.REGISTER_BEAN);
213+
}
214+
215+
@ConditionalOnProperty(prefix = "spring.security.oauth2.resourceserver.jwt", name = "authority-prefix")
216+
static class OnAuthorityPrefix {
217+
218+
}
219+
220+
@ConditionalOnProperty(prefix = "spring.security.oauth2.resourceserver.jwt", name = "principal-claim-name")
221+
static class OnPrincipalClaimName {
222+
223+
}
224+
225+
@ConditionalOnProperty(prefix = "spring.security.oauth2.resourceserver.jwt", name = "authorities-claim-name")
226+
static class OnAuthoritiesClaimName {
227+
228+
}
229+
230+
}
231+
207232
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtConverterCustomizationsArgumentsProvider.java

+2-12
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext extensionCo
4242
String customDelimiter = "[~,#:]";
4343
String customAuthoritiesClaim = "custom_authorities";
4444
String customPrincipalClaim = "custom_principal";
45-
4645
String jwkSetUriProperty = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com";
4746
String authorityPrefixProperty = "spring.security.oauth2.resourceserver.jwt.authority-prefix=" + customPrefix;
4847
String authoritiesDelimiterProperty = "spring.security.oauth2.resourceserver.jwt.authorities-claim-delimiter="
@@ -51,19 +50,15 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext extensionCo
5150
+ customAuthoritiesClaim;
5251
String principalClaimProperty = "spring.security.oauth2.resourceserver.jwt.principal-claim-name="
5352
+ customPrincipalClaim;
54-
55-
String[] noJwtConverterProps = { jwkSetUriProperty };
5653
String[] customPrefixProps = { jwkSetUriProperty, authorityPrefixProperty };
57-
String[] customDelimiterProps = { jwkSetUriProperty, authoritiesDelimiterProperty };
54+
String[] customDelimiterProps = { jwkSetUriProperty, authorityPrefixProperty, authoritiesDelimiterProperty };
5855
String[] customAuthoritiesClaimProps = { jwkSetUriProperty, authoritiesClaimProperty };
5956
String[] customPrincipalClaimProps = { jwkSetUriProperty, principalClaimProperty };
6057
String[] allJwtConverterProps = { jwkSetUriProperty, authorityPrefixProperty, authoritiesDelimiterProperty,
6158
authoritiesClaimProperty, principalClaimProperty };
62-
6359
String[] jwtScopes = { "custom_scope0", "custom_scope1" };
6460
String subjectValue = UUID.randomUUID().toString();
6561
String customPrincipalValue = UUID.randomUUID().toString();
66-
6762
Jwt.Builder jwtBuilder = Jwt.withTokenValue("token")
6863
.header("alg", "none")
6964
.expiresAt(Instant.MAX)
@@ -73,7 +68,6 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext extensionCo
7368
.notBefore(Instant.MIN)
7469
.subject(subjectValue)
7570
.claim(customPrincipalClaim, customPrincipalValue);
76-
7771
Jwt noAuthoritiesCustomizationsJwt = jwtBuilder.claim("scp", jwtScopes[0] + " " + jwtScopes[1]).build();
7872
Jwt customAuthoritiesDelimiterJwt = jwtBuilder.claim("scp", jwtScopes[0] + "~" + jwtScopes[1]).build();
7973
Jwt customAuthoritiesClaimJwt = jwtBuilder.claim("scp", null)
@@ -82,17 +76,13 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext extensionCo
8276
Jwt customAuthoritiesClaimAndDelimiterJwt = jwtBuilder.claim("scp", null)
8377
.claim(customAuthoritiesClaim, jwtScopes[0] + "~" + jwtScopes[1])
8478
.build();
85-
8679
String[] customPrefixAuthorities = { customPrefix + jwtScopes[0], customPrefix + jwtScopes[1] };
8780
String[] defaultPrefixAuthorities = { "SCOPE_" + jwtScopes[0], "SCOPE_" + jwtScopes[1] };
88-
8981
return Stream.of(
90-
Arguments.of(Named.named("No JWT converter customizations", noJwtConverterProps),
91-
noAuthoritiesCustomizationsJwt, subjectValue, defaultPrefixAuthorities),
9282
Arguments.of(Named.named("Custom prefix for GrantedAuthority", customPrefixProps),
9383
noAuthoritiesCustomizationsJwt, subjectValue, customPrefixAuthorities),
9484
Arguments.of(Named.named("Custom delimiter for JWT scopes", customDelimiterProps),
95-
customAuthoritiesDelimiterJwt, subjectValue, defaultPrefixAuthorities),
85+
customAuthoritiesDelimiterJwt, subjectValue, customPrefixAuthorities),
9686
Arguments.of(Named.named("Custom JWT authority claim name", customAuthoritiesClaimProps),
9787
customAuthoritiesClaimJwt, subjectValue, defaultPrefixAuthorities),
9888
Arguments.of(Named.named("Custom JWT principal claim name", customPrincipalClaimProps),

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

+24
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,30 @@ void customValidatorWhenInvalid() throws Exception {
633633
});
634634
}
635635

636+
@Test
637+
void shouldNotConfigureJwtConverterIfNoPropertiesAreSet() {
638+
this.contextRunner
639+
.run((context) -> assertThat(context).doesNotHaveBean(ReactiveJwtAuthenticationConverter.class));
640+
}
641+
642+
@Test
643+
void shouldConfigureJwtConverterIfPrincipalClaimNameIsSet() {
644+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.principal-claim-name=dummy")
645+
.run((context) -> assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class));
646+
}
647+
648+
@Test
649+
void shouldConfigureJwtConverterIfAuthorityPrefixIsSet() {
650+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authority-prefix=dummy")
651+
.run((context) -> assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class));
652+
}
653+
654+
@Test
655+
void shouldConfigureJwtConverterIfAuthorityClaimsNameIsSet() {
656+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-claim-name=dummy")
657+
.run((context) -> assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class));
658+
}
659+
636660
@ParameterizedTest(name = "{0}")
637661
@ArgumentsSource(JwtConverterCustomizationsArgumentsProvider.class)
638662
void autoConfigurationShouldConfigureResourceServerWithJwtConverterCustomizations(String[] properties, Jwt jwt,

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java

+23
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,29 @@ void autoConfigurationShouldConfigureResourceServerWithJwtConverterCustomization
662662
});
663663
}
664664

665+
@Test
666+
void shouldNotConfigureJwtConverterIfNoPropertiesAreSet() {
667+
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(JwtAuthenticationConverter.class));
668+
}
669+
670+
@Test
671+
void shouldConfigureJwtConverterIfPrincipalClaimNameIsSet() {
672+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.principal-claim-name=dummy")
673+
.run((context) -> assertThat(context).hasSingleBean(JwtAuthenticationConverter.class));
674+
}
675+
676+
@Test
677+
void shouldConfigureJwtConverterIfAuthorityPrefixIsSet() {
678+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authority-prefix=dummy")
679+
.run((context) -> assertThat(context).hasSingleBean(JwtAuthenticationConverter.class));
680+
}
681+
682+
@Test
683+
void shouldConfigureJwtConverterIfAuthorityClaimsNameIsSet() {
684+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-claim-name=dummy")
685+
.run((context) -> assertThat(context).hasSingleBean(JwtAuthenticationConverter.class));
686+
}
687+
665688
@Test
666689
void jwtAuthenticationConverterByJwtConfigIsConditionalOnMissingBean() {
667690
String propertiesPrincipalClaim = "principal_from_properties";

0 commit comments

Comments
 (0)