Support JWK Selection Strategy

Closes gh-16170

Signed-off-by: douxiaofeng99 <18600127780@163.com>
This commit is contained in:
douxiaofeng99 2025-02-11 16:36:59 +08:00 committed by Josh Cummings
parent 776eb76c58
commit e22bc11cc9

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -46,6 +46,7 @@ import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.SignedJWT;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -86,6 +87,19 @@ public final class NimbusJwtEncoder implements JwtEncoder {
private final JWKSource<SecurityContext> jwkSource; private final JWKSource<SecurityContext> jwkSource;
private Converter<List<JWK>, JWK> jwkSelector= (jwks)->{
if (jwks.size() > 1) {
throw new JwtEncodingException(String.format(
"Failed to select a key since there are multiple for the signing algorithm [%s]; " +
"please specify a selector in NimbusJwsEncoder#setJwkSelector",jwks.get(0).getAlgorithm()));
}
if (jwks.isEmpty()) {
throw new JwtEncodingException(
String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to select a JWK signing key"));
}
return jwks.get(0);
};
/** /**
* Constructs a {@code NimbusJwtEncoder} using the provided parameters. * Constructs a {@code NimbusJwtEncoder} using the provided parameters.
* @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource} * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}
@ -94,6 +108,18 @@ public final class NimbusJwtEncoder implements JwtEncoder {
Assert.notNull(jwkSource, "jwkSource cannot be null"); Assert.notNull(jwkSource, "jwkSource cannot be null");
this.jwkSource = jwkSource; this.jwkSource = jwkSource;
} }
/**
* Use this strategy to reduce the list of matching JWKs down to a since one.
* <p> For example, you can call {@code setJwkSelector(List::getFirst)} in order
* to have this encoder select the first match.
*
* <p> By default, the class with throw an exception if there is more than one result.
* @since 6.5
*/
public void setJwkSelector(Converter<List<JWK>, JWK> jwkSelector) {
if(null!=jwkSelector)
this.jwkSelector = jwkSelector;
}
@Override @Override
public Jwt encode(JwtEncoderParameters parameters) throws JwtEncodingException { public Jwt encode(JwtEncoderParameters parameters) throws JwtEncodingException {
@ -123,18 +149,7 @@ public final class NimbusJwtEncoder implements JwtEncoder {
throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
"Failed to select a JWK signing key -> " + ex.getMessage()), ex); "Failed to select a JWK signing key -> " + ex.getMessage()), ex);
} }
return this.jwkSelector.convert(jwks);
if (jwks.size() > 1) {
throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
"Found multiple JWK signing keys for algorithm '" + headers.getAlgorithm().getName() + "'"));
}
if (jwks.isEmpty()) {
throw new JwtEncodingException(
String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to select a JWK signing key"));
}
return jwks.get(0);
} }
private String serialize(JwsHeader headers, JwtClaimsSet claims, JWK jwk) { private String serialize(JwsHeader headers, JwtClaimsSet claims, JWK jwk) {