|
|
|
|
@ -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")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|