Skip to content

JwtDecoders and ReactiveJwtDecoders with customizable RestTemplate and WebClient #8690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.client.RestOperations;

import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;

Expand Down Expand Up @@ -283,6 +284,8 @@ public class JwtConfigurer {

private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter;

private RestOperations restOperations;

JwtConfigurer(ApplicationContext context) {
this.context = context;
}
Expand All @@ -299,7 +302,15 @@ public JwtConfigurer decoder(JwtDecoder decoder) {
}

public JwtConfigurer jwkSetUri(String uri) {
this.decoder = withJwkSetUri(uri).build();
final NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = withJwkSetUri(uri);
this.decoder = restOperations == null
? builder.build()
: builder.restOperations(restOperations).build();
return this;
}

public JwtConfigurer restOperations(RestOperations restOperations) {
this.restOperations = restOperations;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.web.client.RestOperations;

/**
* A {@link BeanDefinitionParser} for &lt;http&gt;'s &lt;oauth2-resource-server&gt; element.
Expand Down Expand Up @@ -194,6 +195,7 @@ BeanMetadataElement getEntryPoint(Element element) {
final class JwtBeanDefinitionParser implements BeanDefinitionParser {
static final String DECODER_REF = "decoder-ref";
static final String JWK_SET_URI = "jwk-set-uri";
static final String REST_OPERATIONS_REF = "rest-operations-ref";
static final String JWT_AUTHENTICATION_CONVERTER_REF = "jwt-authentication-converter-ref";
static final String JWT_AUTHENTICATION_CONVERTER = "jwtAuthenticationConverter";

Expand Down Expand Up @@ -228,6 +230,12 @@ Object getDecoder(Element element) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(NimbusJwtDecoderJwkSetUriFactoryBean.class);
builder.addConstructorArgValue(element.getAttribute(JWK_SET_URI));
final String restOperationsRef = element.getAttribute(REST_OPERATIONS_REF);
if (StringUtils.isEmpty(restOperationsRef)) {
builder.addConstructorArgValue(null);
} else {
builder.addConstructorArgReference(restOperationsRef);
}
return builder.getBeanDefinition();
}

Expand Down Expand Up @@ -322,14 +330,19 @@ public AuthenticationManager resolve(HttpServletRequest context) {

final class NimbusJwtDecoderJwkSetUriFactoryBean implements FactoryBean<JwtDecoder> {
private final String jwkSetUri;
private final RestOperations restOperations;

NimbusJwtDecoderJwkSetUriFactoryBean(String jwkSetUri) {
NimbusJwtDecoderJwkSetUriFactoryBean(String jwkSetUri, RestOperations restOperations) {
this.jwkSetUri = jwkSetUri;
this.restOperations = restOperations;
}

@Override
public JwtDecoder getObject() {
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
final NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri);
return restOperations == null
? builder.build()
: builder.restOperations(restOperations).build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
Expand Down Expand Up @@ -174,9 +175,12 @@
import org.springframework.web.cors.reactive.CorsProcessor;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
Expand Down Expand Up @@ -1874,6 +1878,8 @@ public class JwtSpec {
private ReactiveJwtDecoder jwtDecoder;
private Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter
= new ReactiveJwtAuthenticationConverterAdapter(new JwtAuthenticationConverter());
private WebClient webClient;
private String jwkSetUri;

/**
* Configures the {@link ReactiveAuthenticationManager} to use
Expand Down Expand Up @@ -1929,10 +1935,27 @@ public JwtSpec publicKey(RSAPublicKey publicKey) {
* @return the {@code JwtSpec} for additional configuration
*/
public JwtSpec jwkSetUri(String jwkSetUri) {
this.jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);
this.jwkSetUri = jwkSetUri;
this.jwtDecoder = createDecoder();
return this;
}

public JwtSpec webClient(WebClient webClient) {
this.webClient = webClient;
this.jwtDecoder = createDecoder();
return this;
}

private ReactiveJwtDecoder createDecoder() {
if (jwkSetUri != null) {
return webClient == null
? new NimbusReactiveJwtDecoder(jwkSetUri)
: new NimbusReactiveJwtDecoder(jwkSetUri, webClient);
} else {
return null;
}
}

public OAuth2ResourceServerSpec and() {
return OAuth2ResourceServerSpec.this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.core.Authentication
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import java.security.interfaces.RSAPublicKey

Expand All @@ -44,6 +45,7 @@ class ServerJwtDsl {
private var _jwtDecoder: ReactiveJwtDecoder? = null
private var _publicKey: RSAPublicKey? = null
private var _jwkSetUri: String? = null
private var _webClient: WebClient? = null

var authenticationManager: ReactiveAuthenticationManager? = null
var jwtAuthenticationConverter: Converter<Jwt, out Mono<out AbstractAuthenticationToken>>? = null
Expand All @@ -69,14 +71,20 @@ class ServerJwtDsl {
_jwtDecoder = null
_publicKey = null
}
var webClient: WebClient?
get() = _webClient
set(value) {
_webClient = value
}

internal fun get(): (ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec) -> Unit {
return { jwt ->
authenticationManager?.also { jwt.authenticationManager(authenticationManager) }
jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) }
jwtDecoder?.also { jwt.jwtDecoder(jwtDecoder) }
publicKey?.also { jwt.publicKey(publicKey) }
webClient?.also { jwt.webClient(webClient) }
jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) }
jwtDecoder?.also { jwt.jwtDecoder(jwtDecoder) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.web.client.RestOperations

/**
* A Kotlin DSL to configure JWT Resource Server Support using idiomatic Kotlin code.
Expand All @@ -38,6 +39,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoder
class JwtDsl {
private var _jwtDecoder: JwtDecoder? = null
private var _jwkSetUri: String? = null
private var _restOperations: RestOperations? = null

var jwtAuthenticationConverter: Converter<Jwt, out AbstractAuthenticationToken>? = null
var jwtDecoder: JwtDecoder?
Expand All @@ -53,10 +55,17 @@ class JwtDsl {
_jwtDecoder = null
}

var restOperations: RestOperations?
get() = _restOperations
set(value) {
_restOperations = value
}

internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit {
return { jwt ->
jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) }
jwtDecoder?.also { jwt.decoder(jwtDecoder) }
restOperations?.also { jwt.restOperations(restOperations) }
jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) }
}
}
Expand Down
Loading