|
@@ -18,14 +18,10 @@ package org.springframework.security.oauth2.jwt;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.net.MalformedURLException;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.net.URL;
|
|
-import java.text.ParseException;
|
|
|
|
-import java.time.Instant;
|
|
|
|
import java.util.Collections;
|
|
import java.util.Collections;
|
|
-import java.util.LinkedHashMap;
|
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
|
import com.nimbusds.jose.JWSAlgorithm;
|
|
import com.nimbusds.jose.JWSAlgorithm;
|
|
-import com.nimbusds.jose.RemoteKeySourceException;
|
|
|
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
|
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
|
|
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
|
|
import com.nimbusds.jose.proc.JWSKeySelector;
|
|
import com.nimbusds.jose.proc.JWSKeySelector;
|
|
@@ -33,10 +29,6 @@ import com.nimbusds.jose.proc.JWSVerificationKeySelector;
|
|
import com.nimbusds.jose.proc.SecurityContext;
|
|
import com.nimbusds.jose.proc.SecurityContext;
|
|
import com.nimbusds.jose.util.Resource;
|
|
import com.nimbusds.jose.util.Resource;
|
|
import com.nimbusds.jose.util.ResourceRetriever;
|
|
import com.nimbusds.jose.util.ResourceRetriever;
|
|
-import com.nimbusds.jwt.JWT;
|
|
|
|
-import com.nimbusds.jwt.JWTClaimsSet;
|
|
|
|
-import com.nimbusds.jwt.JWTParser;
|
|
|
|
-import com.nimbusds.jwt.SignedJWT;
|
|
|
|
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
|
|
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
|
|
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
|
|
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
|
|
|
|
|
|
@@ -47,7 +39,6 @@ import org.springframework.http.MediaType;
|
|
import org.springframework.http.RequestEntity;
|
|
import org.springframework.http.RequestEntity;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
|
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
|
-import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
|
|
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
|
import org.springframework.util.Assert;
|
|
import org.springframework.util.Assert;
|
|
import org.springframework.web.client.RestOperations;
|
|
import org.springframework.web.client.RestOperations;
|
|
@@ -62,27 +53,24 @@ import org.springframework.web.client.RestTemplate;
|
|
* <p>
|
|
* <p>
|
|
* <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK internally.
|
|
* <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK internally.
|
|
*
|
|
*
|
|
|
|
+ * @deprecated Use {@link NimbusJwtDecoder} instead
|
|
|
|
+ *
|
|
* @author Joe Grandja
|
|
* @author Joe Grandja
|
|
* @author Josh Cummings
|
|
* @author Josh Cummings
|
|
* @since 5.0
|
|
* @since 5.0
|
|
* @see JwtDecoder
|
|
* @see JwtDecoder
|
|
|
|
+ * @see NimbusJwtDecoder
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JSON Web Token (JWT)</a>
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JSON Web Token (JWT)</a>
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature (JWS)</a>
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature (JWS)</a>
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7517">JSON Web Key (JWK)</a>
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7517">JSON Web Key (JWK)</a>
|
|
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus JOSE + JWT SDK</a>
|
|
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus JOSE + JWT SDK</a>
|
|
*/
|
|
*/
|
|
|
|
+@Deprecated
|
|
public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
|
public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
|
- private static final String DECODING_ERROR_MESSAGE_TEMPLATE =
|
|
|
|
- "An error occurred while attempting to decode the Jwt: %s";
|
|
|
|
-
|
|
|
|
private final JWSAlgorithm jwsAlgorithm;
|
|
private final JWSAlgorithm jwsAlgorithm;
|
|
- private final ConfigurableJWTProcessor<SecurityContext> jwtProcessor;
|
|
|
|
private final RestOperationsResourceRetriever jwkSetRetriever = new RestOperationsResourceRetriever();
|
|
private final RestOperationsResourceRetriever jwkSetRetriever = new RestOperationsResourceRetriever();
|
|
|
|
|
|
- private Converter<Map<String, Object>, Map<String, Object>> claimSetConverter =
|
|
|
|
- MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());
|
|
|
|
- private OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefault();
|
|
|
|
-
|
|
|
|
|
|
+ private NimbusJwtDecoder delegate;
|
|
|
|
|
|
/**
|
|
/**
|
|
* Constructs a {@code NimbusJwtDecoderJwkSupport} using the provided parameters.
|
|
* Constructs a {@code NimbusJwtDecoderJwkSupport} using the provided parameters.
|
|
@@ -111,21 +99,18 @@ public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
|
this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
|
|
this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
|
|
JWSKeySelector<SecurityContext> jwsKeySelector =
|
|
JWSKeySelector<SecurityContext> jwsKeySelector =
|
|
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
|
|
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
|
|
- this.jwtProcessor = new DefaultJWTProcessor<>();
|
|
|
|
- this.jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
|
|
|
|
|
+ ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
|
|
|
+ jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
|
|
|
|
|
// Spring Security validates the claim set independent from Nimbus
|
|
// Spring Security validates the claim set independent from Nimbus
|
|
- this.jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {});
|
|
|
|
|
|
+ jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {});
|
|
|
|
+
|
|
|
|
+ this.delegate = new NimbusJwtDecoder(jwtProcessor);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Jwt decode(String token) throws JwtException {
|
|
public Jwt decode(String token) throws JwtException {
|
|
- JWT jwt = this.parse(token);
|
|
|
|
- if (jwt instanceof SignedJWT) {
|
|
|
|
- Jwt createdJwt = this.createJwt(token, jwt);
|
|
|
|
- return this.validateJwt(createdJwt);
|
|
|
|
- }
|
|
|
|
- throw new JwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm());
|
|
|
|
|
|
+ return this.delegate.decode(token);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -135,7 +120,7 @@ public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
|
*/
|
|
*/
|
|
public void setJwtValidator(OAuth2TokenValidator<Jwt> jwtValidator) {
|
|
public void setJwtValidator(OAuth2TokenValidator<Jwt> jwtValidator) {
|
|
Assert.notNull(jwtValidator, "jwtValidator cannot be null");
|
|
Assert.notNull(jwtValidator, "jwtValidator cannot be null");
|
|
- this.jwtValidator = jwtValidator;
|
|
|
|
|
|
+ this.delegate.setJwtValidator(jwtValidator);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -145,57 +130,7 @@ public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
|
*/
|
|
*/
|
|
public final void setClaimSetConverter(Converter<Map<String, Object>, Map<String, Object>> claimSetConverter) {
|
|
public final void setClaimSetConverter(Converter<Map<String, Object>, Map<String, Object>> claimSetConverter) {
|
|
Assert.notNull(claimSetConverter, "claimSetConverter cannot be null");
|
|
Assert.notNull(claimSetConverter, "claimSetConverter cannot be null");
|
|
- this.claimSetConverter = claimSetConverter;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private JWT parse(String token) {
|
|
|
|
- try {
|
|
|
|
- return JWTParser.parse(token);
|
|
|
|
- } catch (Exception ex) {
|
|
|
|
- throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private Jwt createJwt(String token, JWT parsedJwt) {
|
|
|
|
- Jwt jwt;
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- // Verify the signature
|
|
|
|
- JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
|
|
|
|
-
|
|
|
|
- Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
|
|
|
|
- Map<String, Object> claims = this.claimSetConverter.convert(jwtClaimsSet.getClaims());
|
|
|
|
-
|
|
|
|
- Instant expiresAt = (Instant) claims.get(JwtClaimNames.EXP);
|
|
|
|
- Instant issuedAt = (Instant) claims.get(JwtClaimNames.IAT);
|
|
|
|
- jwt = new Jwt(token, issuedAt, expiresAt, headers, claims);
|
|
|
|
- } catch (RemoteKeySourceException ex) {
|
|
|
|
- if (ex.getCause() instanceof ParseException) {
|
|
|
|
- throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, "Malformed Jwk set"));
|
|
|
|
- } else {
|
|
|
|
- throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);
|
|
|
|
- }
|
|
|
|
- } catch (Exception ex) {
|
|
|
|
- if (ex.getCause() instanceof ParseException) {
|
|
|
|
- throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, "Malformed payload"));
|
|
|
|
- } else {
|
|
|
|
- throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return jwt;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private Jwt validateJwt(Jwt jwt){
|
|
|
|
- OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt);
|
|
|
|
- if (result.hasErrors()) {
|
|
|
|
- String description = result.getErrors().iterator().next().getDescription();
|
|
|
|
- throw new JwtValidationException(
|
|
|
|
- String.format(DECODING_ERROR_MESSAGE_TEMPLATE, description),
|
|
|
|
- result.getErrors());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return jwt;
|
|
|
|
|
|
+ this.delegate.setClaimSetConverter(claimSetConverter);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|