Browse Source

Auto-configure ExpressionJwtGrantedAuthoritiesConverters

Closes gh-48490
dependabot/npm_and_yarn/antora/springio/asciidoctor-extensions-1.0.0-alpha.18
Andy Wilkinson 4 days ago
parent
commit
8fff41b3ec
  1. 86
      module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwtConverterConfiguration.java
  2. 16
      module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/OAuth2ResourceServerProperties.java
  3. 104
      module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveJwtConverterConfiguration.java
  4. 68
      module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/OAuth2ResourceServerAutoConfigurationTests.java
  5. 73
      module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

86
module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwtConverterConfiguration.java

@ -16,17 +16,31 @@ @@ -16,17 +16,31 @@
package org.springframework.boot.security.oauth2.server.resource.autoconfigure;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.OnPropertyListCondition;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.DelegatingJwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ExpressionJwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.util.CollectionUtils;
/**
* {@link Configuration @Configuration} for JWT converter beans.
@ -47,18 +61,24 @@ class JwtConverterConfiguration { @@ -47,18 +61,24 @@ class JwtConverterConfiguration {
@Bean
JwtAuthenticationConverter jwtAuthenticationConverter(OAuth2ResourceServerProperties properties) {
return jwtAuthenticationConverter(properties.getJwt());
}
private JwtAuthenticationConverter jwtAuthenticationConverter(OAuth2ResourceServerProperties.Jwt properties) {
PropertyMapper map = PropertyMapper.get();
OAuth2ResourceServerProperties.Jwt jwtProperties = properties.getJwt();
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
map.from(properties::getPrincipalClaimName).to(converter::setPrincipalClaimName);
map.from(properties).as(this::getGrantedAuthoritiesConverter).to(converter::setJwtGrantedAuthoritiesConverter);
map.from(jwtProperties::getPrincipalClaimName).to(converter::setPrincipalClaimName);
map.from(jwtProperties).as(this::grantedAuthoritiesConverter).to(converter::setJwtGrantedAuthoritiesConverter);
return converter;
}
private JwtGrantedAuthoritiesConverter getGrantedAuthoritiesConverter(
private Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter(
OAuth2ResourceServerProperties.Jwt properties) {
List<String> authoritiesExpressions = properties.getAuthoritiesExpressions();
if (CollectionUtils.isEmpty(authoritiesExpressions)) {
return createJwtGrantedAuthoritiesConverter(properties);
}
return createExpressionJwtGrantedAuthoritiesConverters(properties, authoritiesExpressions);
}
private Converter<Jwt, Collection<GrantedAuthority>> createJwtGrantedAuthoritiesConverter(
OAuth2ResourceServerProperties.Jwt properties) {
PropertyMapper map = PropertyMapper.get();
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
@ -68,6 +88,44 @@ class JwtConverterConfiguration { @@ -68,6 +88,44 @@ class JwtConverterConfiguration {
return converter;
}
private Converter<Jwt, Collection<GrantedAuthority>> createExpressionJwtGrantedAuthoritiesConverters(
OAuth2ResourceServerProperties.Jwt properties, List<String> authoritiesExpressions) {
checkMutualExclusivity(properties);
List<Converter<Jwt, Collection<GrantedAuthority>>> converters = new ArrayList<>();
SpelExpressionParser parser = new SpelExpressionParser();
for (String authoritiesExpression : authoritiesExpressions) {
ExpressionJwtGrantedAuthoritiesConverter converter = new ExpressionJwtGrantedAuthoritiesConverter(
parser.parseExpression(authoritiesExpression));
if (properties.getAuthorityPrefix() != null) {
converter.setAuthorityPrefix(properties.getAuthorityPrefix());
}
converters.add(converter);
}
return (converters.size() == 1) ? converters.get(0) : new DelegatingJwtGrantedAuthoritiesConverter(converters);
}
private void checkMutualExclusivity(OAuth2ResourceServerProperties.Jwt properties) {
MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleMatchingValuesIn((entries) -> {
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-expressions",
properties.getAuthoritiesExpressions());
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-claim-name",
properties.getAuthoritiesClaimName());
}, (value) -> !nullOrEmptyList(value));
MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleMatchingValuesIn((entries) -> {
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-expressions",
properties.getAuthoritiesExpressions());
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-claim-delimiter",
properties.getAuthoritiesClaimDelimiter());
}, (value) -> !nullOrEmptyList(value));
}
private boolean nullOrEmptyList(Object value) {
if (value == null) {
return true;
}
return (value instanceof List<?> list) ? list.isEmpty() : false;
}
static class PropertiesCondition extends AnyNestedCondition {
PropertiesCondition() {
@ -89,6 +147,20 @@ class JwtConverterConfiguration { @@ -89,6 +147,20 @@ class JwtConverterConfiguration {
}
@Conditional(OnAuthoritiesExpressionsCondition.class)
static class OnAuthoritiesExpressions {
}
static class OnAuthoritiesExpressionsCondition extends OnPropertyListCondition {
OnAuthoritiesExpressionsCondition() {
super("spring.security.oauth2.resourceserver.jwt.authorities-expressions",
() -> ConditionMessage.forCondition("Authorities expressions"));
}
}
}
}

16
module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/OAuth2ResourceServerProperties.java

@ -98,6 +98,14 @@ public class OAuth2ResourceServerProperties { @@ -98,6 +98,14 @@ public class OAuth2ResourceServerProperties {
*/
private @Nullable String authoritiesClaimName;
/**
* List of expressions to use to extract authorities from a JWT. Mutually
* exclusive with
* 'spring.security.oauth2.resourceserver.jwt.authorities-claim-name' and
* 'spring.security.oauth2.resourceserver.jwt.authorities-claim-delimiter'.
*/
private List<String> authoritiesExpressions = new ArrayList<>();
/**
* JWT principal claim name.
*/
@ -167,6 +175,14 @@ public class OAuth2ResourceServerProperties { @@ -167,6 +175,14 @@ public class OAuth2ResourceServerProperties {
this.authoritiesClaimName = authoritiesClaimName;
}
public List<String> getAuthoritiesExpressions() {
return this.authoritiesExpressions;
}
public void setAuthoritiesExpressions(List<String> authoritiesExpressions) {
this.authoritiesExpressions = authoritiesExpressions;
}
public @Nullable String getPrincipalClaimName() {
return this.principalClaimName;
}

104
module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveJwtConverterConfiguration.java

@ -16,19 +16,33 @@ @@ -16,19 +16,33 @@
package org.springframework.boot.security.oauth2.server.resource.autoconfigure.reactive;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.OnPropertyListCondition;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
import org.springframework.boot.security.oauth2.server.resource.autoconfigure.OAuth2ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.DelegatingJwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ExpressionJwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtGrantedAuthoritiesConverterAdapter;
import org.springframework.util.CollectionUtils;
/**
* {@link Configuration @Configuration} for JWT converter beans.
@ -50,24 +64,74 @@ class ReactiveJwtConverterConfiguration { @@ -50,24 +64,74 @@ class ReactiveJwtConverterConfiguration {
@Bean
ReactiveJwtAuthenticationConverter reactiveJwtAuthenticationConverter(OAuth2ResourceServerProperties properties) {
return reactiveJwtAuthenticationConverter(properties.getJwt());
}
private ReactiveJwtAuthenticationConverter reactiveJwtAuthenticationConverter(
OAuth2ResourceServerProperties.Jwt properties1) {
PropertyMapper map = PropertyMapper.get();
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
map.from(properties1.getAuthorityPrefix()).to(grantedAuthoritiesConverter::setAuthorityPrefix);
map.from(properties1.getAuthoritiesClaimDelimiter())
.to(grantedAuthoritiesConverter::setAuthoritiesClaimDelimiter);
map.from(properties1.getAuthoritiesClaimName()).to(grantedAuthoritiesConverter::setAuthoritiesClaimName);
OAuth2ResourceServerProperties.Jwt jwtProperties = properties.getJwt();
ReactiveJwtAuthenticationConverter jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();
map.from(properties1.getPrincipalClaimName()).to(jwtAuthenticationConverter::setPrincipalClaimName);
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(
new ReactiveJwtGrantedAuthoritiesConverterAdapter(grantedAuthoritiesConverter));
map.from(jwtProperties::getPrincipalClaimName).to(jwtAuthenticationConverter::setPrincipalClaimName);
map.from(jwtProperties)
.as(this::grantedAuthoritiesConverter)
.as(ReactiveJwtGrantedAuthoritiesConverterAdapter::new)
.to(jwtAuthenticationConverter::setJwtGrantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
private Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter(
OAuth2ResourceServerProperties.Jwt properties) {
List<String> authoritiesExpressions = properties.getAuthoritiesExpressions();
if (CollectionUtils.isEmpty(authoritiesExpressions)) {
return createJwtGrantedAuthoritiesConverter(properties);
}
return createExpressionJwtGrantedAuthoritiesConverters(properties, authoritiesExpressions);
}
private Converter<Jwt, Collection<GrantedAuthority>> createJwtGrantedAuthoritiesConverter(
OAuth2ResourceServerProperties.Jwt properties) {
PropertyMapper map = PropertyMapper.get();
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
map.from(properties::getAuthorityPrefix).to(converter::setAuthorityPrefix);
map.from(properties::getAuthoritiesClaimDelimiter).to(converter::setAuthoritiesClaimDelimiter);
map.from(properties::getAuthoritiesClaimName).to(converter::setAuthoritiesClaimName);
return converter;
}
private Converter<Jwt, Collection<GrantedAuthority>> createExpressionJwtGrantedAuthoritiesConverters(
OAuth2ResourceServerProperties.Jwt properties, List<String> authoritiesExpressions) {
checkMutualExclusivity(properties);
List<Converter<Jwt, Collection<GrantedAuthority>>> converters = new ArrayList<>();
SpelExpressionParser parser = new SpelExpressionParser();
for (String authoritiesExpression : authoritiesExpressions) {
ExpressionJwtGrantedAuthoritiesConverter converter = new ExpressionJwtGrantedAuthoritiesConverter(
parser.parseExpression(authoritiesExpression));
if (properties.getAuthorityPrefix() != null) {
converter.setAuthorityPrefix(properties.getAuthorityPrefix());
}
converters.add(converter);
}
return (converters.size() == 1) ? converters.get(0) : new DelegatingJwtGrantedAuthoritiesConverter(converters);
}
private void checkMutualExclusivity(OAuth2ResourceServerProperties.Jwt properties) {
MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleMatchingValuesIn((entries) -> {
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-expressions",
properties.getAuthoritiesExpressions());
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-claim-name",
properties.getAuthoritiesClaimName());
}, (value) -> !nullOrEmptyList(value));
MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleMatchingValuesIn((entries) -> {
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-expressions",
properties.getAuthoritiesExpressions());
entries.put("spring.security.oauth2.resourceserver.jwt.authorities-claim-delimiter",
properties.getAuthoritiesClaimDelimiter());
}, (value) -> !nullOrEmptyList(value));
}
private boolean nullOrEmptyList(Object value) {
if (value == null) {
return true;
}
return (value instanceof List<?> list) ? list.isEmpty() : false;
}
static class PropertiesCondition extends AnyNestedCondition {
PropertiesCondition() {
@ -89,6 +153,20 @@ class ReactiveJwtConverterConfiguration { @@ -89,6 +153,20 @@ class ReactiveJwtConverterConfiguration {
}
@Conditional(OnAuthoritiesExpressionsCondition.class)
static class OnAuthoritiesExpressions {
}
static class OnAuthoritiesExpressionsCondition extends OnPropertyListCondition {
OnAuthoritiesExpressionsCondition() {
super("spring.security.oauth2.resourceserver.jwt.authorities-expressions",
() -> ConditionMessage.forCondition("Authorities expressions"));
}
}
}
}

68
module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/OAuth2ResourceServerAutoConfigurationTests.java

@ -47,6 +47,7 @@ import tools.jackson.databind.json.JsonMapper; @@ -47,6 +47,7 @@ import tools.jackson.databind.json.JsonMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@ -74,6 +75,7 @@ import org.springframework.security.oauth2.jwt.JwtTypeValidator; @@ -74,6 +75,7 @@ import org.springframework.security.oauth2.jwt.JwtTypeValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.SupplierJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.ExpressionJwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.web.SecurityFilterChain;
@ -689,6 +691,72 @@ class OAuth2ResourceServerAutoConfigurationTests { @@ -689,6 +691,72 @@ class OAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(context).hasSingleBean(JwtAuthenticationConverter.class));
}
@Test
void shouldConfigureJwtConverterIfAuthoritiesExpressionIsSet() {
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions=zero")
.run((context) -> {
assertThat(context).hasSingleBean(JwtAuthenticationConverter.class);
JwtAuthenticationConverter converter = context.getBean(JwtAuthenticationConverter.class);
assertThat(converter).extracting("jwtGrantedAuthoritiesConverter")
.isInstanceOf(ExpressionJwtGrantedAuthoritiesConverter.class)
.extracting("authorityPrefix")
.isEqualTo("SCOPE_");
});
}
@Test
void shouldConfigureJwtConverterIfAuthoritiesExpressionsAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-expressions[1]=one")
.run((context) -> {
assertThat(context).hasSingleBean(JwtAuthenticationConverter.class);
JwtAuthenticationConverter converter = context.getBean(JwtAuthenticationConverter.class);
assertThat(converter)
.extracting("jwtGrantedAuthoritiesConverter.authoritiesConverters", InstanceOfAssertFactories.LIST)
.hasSize(2)
.extracting("authorityPrefix")
.containsOnly("SCOPE_");
});
}
@Test
void shouldApplyCustomAuthorityPrefixIfAuthoritiesExpressionsAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-expressions[1]=one",
"spring.security.oauth2.resourceserver.jwt.authority-prefix=CUSTOM_")
.run((context) -> {
assertThat(context).hasSingleBean(JwtAuthenticationConverter.class);
JwtAuthenticationConverter converter = context.getBean(JwtAuthenticationConverter.class);
assertThat(converter)
.extracting("jwtGrantedAuthoritiesConverter.authoritiesConverters", InstanceOfAssertFactories.LIST)
.hasSize(2)
.extracting("authorityPrefix")
.containsOnly("CUSTOM_");
});
}
@Test
void shouldFailIfBothAuthoritiesExpressionsAndAuthoritiesClaimDelimiterAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-claim-delimiter=delimiter")
.run((context) -> assertThat(context).getFailure()
.rootCause()
.isInstanceOf(MutuallyExclusiveConfigurationPropertiesException.class));
}
@Test
void shouldFailIfBothAuthoritiesExpressionsAndAuthoritiesClaimNameAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-claim-name=name")
.run((context) -> assertThat(context).getFailure()
.rootCause()
.isInstanceOf(MutuallyExclusiveConfigurationPropertiesException.class));
}
@Test
void jwtAuthenticationConverterByJwtConfigIsConditionalOnMissingBean() {
String propertiesPrincipalClaim = "principal_from_properties";

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

@ -48,6 +48,7 @@ import reactor.core.publisher.Mono; @@ -48,6 +48,7 @@ import reactor.core.publisher.Mono;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
import org.springframework.boot.security.oauth2.server.resource.autoconfigure.JwtConverterCustomizationsArgumentsProvider;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
@ -77,6 +78,7 @@ import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; @@ -77,6 +78,7 @@ 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.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.ExpressionJwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.security.web.server.SecurityWebFilterChain;
@ -678,6 +680,77 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { @@ -678,6 +680,77 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class));
}
@Test
void shouldConfigureJwtConverterIfAuthoritiesExpressionIsSet() {
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions=zero")
.run((context) -> {
assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class);
ReactiveJwtAuthenticationConverter converter = context
.getBean(ReactiveJwtAuthenticationConverter.class);
assertThat(converter).extracting("jwtGrantedAuthoritiesConverter.grantedAuthoritiesConverter")
.isInstanceOf(ExpressionJwtGrantedAuthoritiesConverter.class)
.extracting("authorityPrefix")
.isEqualTo("SCOPE_");
});
}
@Test
void shouldConfigureJwtConverterIfAuthoritiesExpressionsAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-expressions[1]=one")
.run((context) -> {
assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class);
ReactiveJwtAuthenticationConverter converter = context
.getBean(ReactiveJwtAuthenticationConverter.class);
assertThat(converter)
.extracting("jwtGrantedAuthoritiesConverter.grantedAuthoritiesConverter.authoritiesConverters",
InstanceOfAssertFactories.LIST)
.hasSize(2)
.extracting("authorityPrefix")
.containsOnly("SCOPE_");
});
}
@Test
void shouldApplyCustomAuthorityPrefixIfAuthoritiesExpressionsAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-expressions[1]=one",
"spring.security.oauth2.resourceserver.jwt.authority-prefix=CUSTOM_")
.run((context) -> {
assertThat(context).hasSingleBean(ReactiveJwtAuthenticationConverter.class);
ReactiveJwtAuthenticationConverter converter = context
.getBean(ReactiveJwtAuthenticationConverter.class);
assertThat(converter)
.extracting("jwtGrantedAuthoritiesConverter.grantedAuthoritiesConverter.authoritiesConverters",
InstanceOfAssertFactories.LIST)
.hasSize(2)
.extracting("authorityPrefix")
.containsOnly("CUSTOM_");
});
}
@Test
void shouldFailIfBothAuthoritiesExpressionsAndAuthoritiesClaimDelimiterAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-claim-delimiter=delimiter")
.run((context) -> assertThat(context).getFailure()
.rootCause()
.isInstanceOf(MutuallyExclusiveConfigurationPropertiesException.class));
}
@Test
void shouldFailIfBothAuthoritiesExpressionsAndAuthoritiesClaimNameAreSet() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.authorities-expressions[0]=zero",
"spring.security.oauth2.resourceserver.jwt.authorities-claim-name=name")
.run((context) -> assertThat(context).getFailure()
.rootCause()
.isInstanceOf(MutuallyExclusiveConfigurationPropertiesException.class));
}
@ParameterizedTest(name = "{0}")
@ArgumentsSource(JwtConverterCustomizationsArgumentsProvider.class)
void autoConfigurationShouldConfigureResourceServerWithJwtConverterCustomizations(String[] properties, Jwt jwt,

Loading…
Cancel
Save