From 62b8cb8118c075598d303cd3b73fe6a8d3cbae42 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Mon, 5 Aug 2024 19:04:51 +0800 Subject: [PATCH] refactor: moved MapUtil implemented by reflect API to another package --- .../com/onixbyte/devkit/utils/AesUtil.java | 11 +- .../com/onixbyte/devkit/utils/Base64Util.java | 2 +- .../devkit/utils/ChainedCalcUtil.java | 7 +- .../com/onixbyte/devkit/utils/HashUtil.java | 2 +- .../com/onixbyte/devkit/utils/MapUtil.java | 3 +- .../java/com/onixbyte/security/KeyLoader.java | 12 +-- .../exception/KeyLoadingException.java | 41 ++++++- .../devkit/utils/unsafe/ReflectMapUtil.java | 5 +- simple-jwt-authzero/build.gradle.kts | 1 + .../authzero/AuthzeroTokenResolver.java | 102 ++++++++++-------- .../config/AuthzeroTokenResolverConfig.java | 42 ++++---- .../simplejwt/config/TokenResolverConfig.java | 14 ++- .../simplejwt/constants/PredefinedKeys.java | 14 +-- .../simplejwt/jjwt/JjwtTokenResolver.java | 6 -- ...uthzeroTokenResolverAutoConfiguration.java | 65 ++++++----- .../properties/SimpleJwtProperties.java | 7 ++ 16 files changed, 204 insertions(+), 130 deletions(-) rename {devkit-utils => map-util-unsafe}/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java (98%) diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java index bc9ec22..14baad1 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java @@ -34,8 +34,7 @@ import java.util.Objects; import java.util.UUID; /** - * {@link AesUtil} can help you encrypt and decrypt data with specified secret - * by AES algorithm. + * {@link AesUtil} can help you encrypt and decrypt data with specified secret by AES algorithm. * * @author hubin@baomidou * @version 1.1.0 @@ -81,8 +80,9 @@ public final class AesUtil { var cipher = Cipher.getInstance(AES_CBC_CIPHER); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret)); return cipher.doFinal(data); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedOperationException | - InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | + } catch (NoSuchAlgorithmException | NoSuchPaddingException | + UnsupportedOperationException | InvalidKeyException | + InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException exception) { log.error(exception.getMessage()); for (var stackTraceElement : exception.getStackTrace()) { @@ -100,7 +100,8 @@ public final class AesUtil { * @return the encryption result or {@code null} if encryption failed */ public static String encrypt(String data, String secret) { - return Base64.getEncoder().encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8), secret.getBytes(StandardCharsets.UTF_8))); + return Base64.getEncoder().encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8), + secret.getBytes(StandardCharsets.UTF_8))); } /** diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java index 41a0cc3..18802bf 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java @@ -49,7 +49,7 @@ import java.util.Objects; * provided. It is recommended to specify the charset explicitly to ensure consistent * encoding and decoding. * - * @author Zihlu Wang + * @author zihluwang * @version 1.1.0 * @since 1.0.0 */ diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ChainedCalcUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ChainedCalcUtil.java index a8f395e..2ee3092 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ChainedCalcUtil.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ChainedCalcUtil.java @@ -293,7 +293,9 @@ public final class ChainedCalcUtil { * or null if not applicable * @return a ChainedCalcUtil instance with the updated value */ - private ChainedCalcUtil operator(BiFunction operator, Object other, Integer beforeOperateScale) { + private ChainedCalcUtil operator(BiFunction operator, + Object other, + Integer beforeOperateScale) { return baseOperator((otherValue) -> operator.apply(this.value, otherValue), other, @@ -311,7 +313,8 @@ public final class ChainedCalcUtil { * @return a ChainedCalcUtil instance with the updated value */ private synchronized ChainedCalcUtil baseOperator(Function operatorFunction, - Object anotherValue, Integer beforeOperateScale) { + Object anotherValue, + Integer beforeOperateScale) { if (Objects.isNull(anotherValue)) { return this; } diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/HashUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/HashUtil.java index f0e4c59..2c24283 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/HashUtil.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/HashUtil.java @@ -61,7 +61,7 @@ import java.util.Optional; * for data integrity checks and password storage, but they should not be used for * encryption purposes. * - * @author Zihlu Wang + * @author zihluwang * @version 1.1.0 * @see java.security.MessageDigest * @since 1.0.0 diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/MapUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/MapUtil.java index aa23699..195020c 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/MapUtil.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/MapUtil.java @@ -30,9 +30,8 @@ import java.util.Optional; * Note: Since version 1.4.2, this util class removed reflection API and transferred to a safer API. * Please see documentation for more information. * - * @author Zihlu Wang + * @author zihluwang * @version 1.4.2 - * @see com.onixbyte.devkit.utils.unsafe.ReflectMapUtil * @since 1.0.0 */ @Slf4j diff --git a/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java index 0e3c8ac..4a4b3e1 100644 --- a/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java +++ b/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java @@ -50,10 +50,10 @@ public class KeyLoader { * * @param pemKeyText pem-formatted key text * @return loaded private key - * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance, or EC Key Factory is - * not loaded, or key spec is invalid + * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance, + * or EC Key Factory is not loaded, or key spec is invalid */ - public ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) { + public static ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) { try { var decodedKeyString = Base64.getDecoder().decode(pemKeyText); var keySpec = new PKCS8EncodedKeySpec(decodedKeyString); @@ -76,10 +76,10 @@ public class KeyLoader { * * @param pemKeyText pem-formatted key text * @return loaded private key - * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance, or EC Key Factory is - * not loaded, or key spec is invalid + * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance, + * or EC Key Factory is not loaded, or key spec is invalid */ - public ECPublicKey loadEcdsaPublicKey(String pemKeyText) { + public static ECPublicKey loadEcdsaPublicKey(String pemKeyText) { try { var keyBytes = Base64.getDecoder().decode(pemKeyText); var spec = new X509EncodedKeySpec(keyBytes); diff --git a/key-pair-loader/src/main/java/com/onixbyte/security/exception/KeyLoadingException.java b/key-pair-loader/src/main/java/com/onixbyte/security/exception/KeyLoadingException.java index 4682d67..2c36b10 100644 --- a/key-pair-loader/src/main/java/com/onixbyte/security/exception/KeyLoadingException.java +++ b/key-pair-loader/src/main/java/com/onixbyte/security/exception/KeyLoadingException.java @@ -17,24 +17,63 @@ package com.onixbyte.security.exception; +/** + * {@code KeyLoadingException} is an exception indicating an error occurred while loading a key. + * + * @author zihluwang + * @version 1.6.0 + * @since 1.6.0 + */ public class KeyLoadingException extends RuntimeException { + /** + * Creates a new instance of {@code KeyLoadingException} without a specific message or cause. + */ public KeyLoadingException() { } + /** + * Creates a new instance of {@code KeyLoadingException} with the specified detail message. + * + * @param message the detail message + */ public KeyLoadingException(String message) { super(message); } + /** + * Creates a new instance of {@code KeyLoadingException} with the specified detail message + * and cause. + * + * @param message the detail message + * @param cause the cause of this exception + */ public KeyLoadingException(String message, Throwable cause) { super(message, cause); } + /** + * Creates a new instance of {@code KeyLoadingException} with the specified cause. + * + * @param cause the cause of this exception + */ public KeyLoadingException(Throwable cause) { super(cause); } - public KeyLoadingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + /** + * Constructs a new exception with the specified detail message, cause, suppression enabled + * or disabled, and writable stack trace enabled or disabled. + * + * @param message the detail message + * @param cause the cause of this exception + * @param enableSuppression whether suppression is enabled or disabled + * @param writableStackTrace whether the stack trace should be writable + */ + public KeyLoadingException(String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java b/map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java similarity index 98% rename from devkit-utils/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java rename to map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java index 11815ed..daa84c5 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java +++ b/map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java @@ -53,7 +53,7 @@ public final class ReflectMapUtil { var declaredFields = obj.getClass().getDeclaredFields(); for (var field : declaredFields) { field.setAccessible(true); - Object result = field.get(obj); + var result = field.get(obj); if (result != null) { map.put(field.getName(), result); } @@ -157,7 +157,8 @@ public final class ReflectMapUtil { * @throws IllegalAccessException if an error occurs while accessing the field * @throws NoSuchMethodException if the specific setter is not present */ - public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + public static void setFieldValue(Object obj, String fieldName, Object fieldValue) + throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { var objectClass = obj.getClass(); var methodName = getMethodName("set", fieldName); var method = objectClass.getDeclaredMethod(methodName, fieldValue.getClass()); diff --git a/simple-jwt-authzero/build.gradle.kts b/simple-jwt-authzero/build.gradle.kts index b6f85b3..55a62ea 100644 --- a/simple-jwt-authzero/build.gradle.kts +++ b/simple-jwt-authzero/build.gradle.kts @@ -33,6 +33,7 @@ version = buildVersion dependencies { implementation(project(":devkit-utils")) implementation(project(":guid")) + implementation(project(":key-pair-loader")) implementation(project(":simple-jwt-facade")) implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") implementation("com.auth0:java-jwt:$javaJwtVersion") diff --git a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java index da627b5..cf97ac5 100644 --- a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java +++ b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java @@ -25,6 +25,7 @@ import com.onixbyte.simplejwt.TokenResolver; import com.onixbyte.simplejwt.annotations.ExcludeFromPayload; import com.onixbyte.simplejwt.annotations.TokenEnum; import com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig; +import com.onixbyte.simplejwt.config.TokenResolverConfig; import com.onixbyte.simplejwt.constants.PredefinedKeys; import com.onixbyte.simplejwt.constants.TokenAlgorithm; import com.auth0.jwt.JWT; @@ -45,15 +46,13 @@ import java.time.ZoneId; import java.util.*; /** - * The {@code AuthzeroTokenResolver} class is an implementation of the {@link - * TokenResolver} interface. It uses the {@code - * com.auth0:java-jwt} library to handle JSON Web Token (JWT) resolution. This - * resolver provides functionality to create, extract, verify, and renew JWT + * The {@code AuthzeroTokenResolver} class is an implementation of the {@link TokenResolver} + * interface. It uses the {@code com.auth0:java-jwt} library to handle JSON Web Token (JWT) + * resolution. This resolver provides functionality to create, extract, verify, and renew JWT * tokens using various algorithms and custom payload data. *

* Usage: - * To use the {@code AuthzeroTokenResolver}, first, create an instance of this - * class: + * To use the {@code AuthzeroTokenResolver}, first, create an instance of this class: *

{@code
  * TokenResolver tokenResolver =
  *     new AuthzeroTokenResolver(TokenAlgorithm.HS256,
@@ -62,8 +61,7 @@ import java.util.*;
  *                               "Token Secret");
  * }
*

- * Then, you can utilize the various methods provided by this resolver to - * handle JWT tokens: + * Then, you can utilize the various methods provided by this resolver to handle JWT tokens: *

{@code
  * // Creating a new JWT token
  * String token =
@@ -82,10 +80,9 @@ import java.util.*;
  * }
*

* Note: - * It is essential to configure the appropriate algorithms, secret, and issuer - * according to your specific use case when using this resolver. - * Additionally, ensure that the {@code com.auth0:java-jwt} library is - * correctly configured in your project's dependencies. + * It is essential to configure the appropriate algorithms, secret, and issuer according to your + * specific use case when using this resolver. Additionally, ensure that the + * {@code com.auth0:java-jwt} library is correctly configured in your project's dependencies. * * @author Zihlu Wang * @version 1.1.1 @@ -100,61 +97,77 @@ import java.util.*; public class AuthzeroTokenResolver implements TokenResolver { /** - * Creates a new instance of {@code AuthzeroTokenResolver} with the - * provided configurations. + * Creates a new instance of {@code AuthzeroTokenResolver} with the provided configurations. * - * @param jtiCreator the {@link GuidCreator} used for generating unique - * identifiers for "jti" claim in JWT tokens - * @param algorithm the algorithm used for signing and verifying JWT - * tokens + * @param jtiCreator the {@link GuidCreator} used for generating unique identifiers for "jti" + * claim in JWT tokens + * @param algorithm the algorithm used for signing and verifying JWT tokens * @param issuer the issuer claim value to be included in JWT tokens - * @param secret the secret used for HMAC-based algorithms (HS256, - * HS384, HS512) for token signing and verification + * @param privateKey the secret used for HMAC-based algorithms (HS256, HS384, HS512) for + * token signing and verification, or the private key for ECDSA-based + * algorithms + * @param publicKey the public key for ECDSA-based algorithms * @param objectMapper JSON handler */ - public AuthzeroTokenResolver(GuidCreator jtiCreator, TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) { - if (secret == null || secret.isBlank()) { - throw new IllegalArgumentException("A secret is required to build a JSON Web Token."); - } + public AuthzeroTokenResolver(GuidCreator jtiCreator, + TokenAlgorithm algorithm, + String issuer, + String privateKey, + String publicKey, + ObjectMapper objectMapper) { + if (TokenResolverConfig.HMAC_ALGORITHMS.contains(algorithm)) { + if (privateKey == null || privateKey.isBlank()) { + throw new IllegalArgumentException("A secret is required to build a JSON Web Token."); + } - if (secret.length() < 32) { - log.warn("The provided secret which owns {} characters is too weak. Please consider replacing it with a stronger one.", secret.length()); + if (privateKey.length() < 32) { + log.warn("The provided secret which owns {} characters is too weak. Please consider" + + " replacing it with a stronger one.", privateKey.length()); + } } this.jtiCreator = jtiCreator; this.algorithm = config .getAlgorithm(algorithm) - .apply(secret); + .apply(privateKey, publicKey); this.issuer = issuer; this.verifier = JWT.require(this.algorithm).build(); this.objectMapper = objectMapper; } /** - * Creates a new instance of {@link AuthzeroTokenResolver} with the - * provided configurations and a simple UUID GuidCreator. + * Creates a new instance of {@link AuthzeroTokenResolver} with the provided configurations + * and a simple UUID GuidCreator. * * @param algorithm the algorithm used for signing and verifying JWT tokens * @param issuer the issuer claim value to be included in JWT tokens - * @param secret the secret used for HMAC-based algorithms (HS256, - * HS384, HS512) for token signing and verification + * @param privateKey the secret used for HMAC-based algorithms (HS256, HS384, HS512) for + * token signing and verification, or the private key for ECDSA-based + * algorithms + * @param publicKey the public key for ECDSA-based algorithms * @param objectMapper Jackson Databind JSON Handler */ - public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) { - this(UUID::randomUUID, algorithm, issuer, secret, objectMapper); + public AuthzeroTokenResolver(TokenAlgorithm algorithm, + String issuer, + String privateKey, + String publicKey, + ObjectMapper objectMapper) { + this(UUID::randomUUID, algorithm, issuer, privateKey, publicKey, objectMapper); } /** - * Creates a new instance of {@link AuthzeroTokenResolver} with the - * provided configurations and a simple UUID GuidCreator. + * Creates a new instance of {@link AuthzeroTokenResolver} with the provided configurations + * and a simple UUID GuidCreator. * - * @param algorithm the algorithm used for signing and verifying JWT tokens - * @param issuer the issuer claim value to be included in JWT tokens - * @param secret the secret used for HMAC-based algorithms (HS256, - * HS384, HS512) for token signing and verification + * @param algorithm the algorithm used for signing and verifying JWT tokens + * @param issuer the issuer claim value to be included in JWT tokens + * @param privateKey the secret used for HMAC-based algorithms (HS256, HS384, HS512) for + * token signing and verification, or the private key for ECDSA-based + * algorithms + * @param publicKey the public key for ECDSA-based algorithms */ - public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret) { - this(UUID::randomUUID, algorithm, issuer, secret, new ObjectMapper()); + public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String privateKey, String publicKey) { + this(UUID::randomUUID, algorithm, issuer, privateKey, publicKey, new ObjectMapper()); } /** @@ -163,11 +176,10 @@ public class AuthzeroTokenResolver implements TokenResolver { * UUID GuidCreator. * * @param issuer the issuer claim value to be included in JWT tokens - * @param secret the secret used for HMAC-based algorithms (HS256, - * HS384, HS512) for token signing and verification + * @param secret the secret used for HS256 algorithms for token signing and verification */ public AuthzeroTokenResolver(String issuer, String secret) { - this(UUID::randomUUID, TokenAlgorithm.HS256, issuer, secret, new ObjectMapper()); + this(UUID::randomUUID, TokenAlgorithm.HS256, issuer, secret, "", new ObjectMapper()); } /** @@ -183,7 +195,7 @@ public class AuthzeroTokenResolver implements TokenResolver { this.jtiCreator = UUID::randomUUID; this.algorithm = config .getAlgorithm(TokenAlgorithm.HS256) - .apply(secret); + .apply(secret, ""); this.issuer = issuer; this.verifier = JWT.require(this.algorithm).build(); this.objectMapper = new ObjectMapper(); diff --git a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java index 768b8ce..829c8ad 100644 --- a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java +++ b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java @@ -17,6 +17,7 @@ package com.onixbyte.simplejwt.authzero.config; +import com.onixbyte.security.KeyLoader; import com.onixbyte.simplejwt.TokenResolver; import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver; import com.onixbyte.simplejwt.config.TokenResolverConfig; @@ -30,6 +31,7 @@ import java.security.interfaces.ECPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; +import java.util.function.BiFunction; import java.util.function.Function; /** @@ -58,7 +60,7 @@ import java.util.function.Function; * @since 1.0.0 */ public final class AuthzeroTokenResolverConfig - implements TokenResolverConfig> { + implements TokenResolverConfig> { /** * Gets the instance of {@code AuthzeroTokenResolverConfig}. @@ -92,7 +94,7 @@ public final class AuthzeroTokenResolverConfig * this implementation */ @Override - public Function getAlgorithm(TokenAlgorithm algorithm) { + public BiFunction getAlgorithm(TokenAlgorithm algorithm) { return Optional.of(SUPPORTED_ALGORITHMS).map((entry) -> entry.get(algorithm)) .orElseThrow(() -> new UnsupportedAlgorithmException("The specified algorithm is not supported yet.")); } @@ -127,26 +129,20 @@ public final class AuthzeroTokenResolverConfig * specific algorithms. The mapping is used to provide proper algorithm * resolution and processing within the {@link AuthzeroTokenResolver}. */ - private static final Map> SUPPORTED_ALGORITHMS = new HashMap<>() {{ - put(TokenAlgorithm.HS256, Algorithm::HMAC256); - put(TokenAlgorithm.HS384, Algorithm::HMAC384); - put(TokenAlgorithm.HS512, Algorithm::HMAC512); - put(TokenAlgorithm.ES256, (String privateKey) -> { - try { - var keyBytes = Base64.getDecoder().decode(privateKey); - var spec = new PKCS8EncodedKeySpec(keyBytes); - var kf = KeyFactory.getInstance("EC"); - var key = kf.generatePrivate(spec); - if (key instanceof ECPrivateKey pk) { - return Algorithm.ECDSA256(pk); - } else { - throw new RuntimeException("Type error!"); - } - } catch (NoSuchAlgorithmException ignored) { - } catch (InvalidKeySpecException e) { - throw new RuntimeException(e); - } - return null; - }); + private static final + Map> SUPPORTED_ALGORITHMS = + new HashMap<>() {{ + put(TokenAlgorithm.HS256, (String secret, String ignoredValue) -> + Algorithm.HMAC256(secret)); + put(TokenAlgorithm.HS384, (String secret, String ignoredValue) -> + Algorithm.HMAC384(secret)); + put(TokenAlgorithm.HS512, (String secret, String ignoredValue) -> + Algorithm.HMAC512(secret)); + put(TokenAlgorithm.ES256, (String privateKey, String publicKey) -> + Algorithm.ECDSA256(KeyLoader.loadEcdsaPrivateKey(privateKey))); + put(TokenAlgorithm.ES384, (String privateKey, String publicKey) -> + Algorithm.ECDSA256(KeyLoader.loadEcdsaPrivateKey(privateKey))); + put(TokenAlgorithm.ES512, (String privateKey, String publicKey) -> + Algorithm.ECDSA256(KeyLoader.loadEcdsaPrivateKey(privateKey))); }}; } diff --git a/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/config/TokenResolverConfig.java b/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/config/TokenResolverConfig.java index 2252563..6b961d8 100644 --- a/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/config/TokenResolverConfig.java +++ b/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/config/TokenResolverConfig.java @@ -20,6 +20,8 @@ package com.onixbyte.simplejwt.config; import com.onixbyte.simplejwt.TokenResolver; import com.onixbyte.simplejwt.constants.TokenAlgorithm; +import java.util.List; + /** * The {@code TokenResolverConfig} provides a mechanism to configure an * implementation of {@link TokenResolver} with algorithm functions. @@ -49,11 +51,15 @@ public interface TokenResolverConfig { * implementation that can be used by the {@link TokenResolver} to handle * the specific algorithm. * - * @param algorithm the {@link TokenAlgorithm} for which the algorithm - * function is required - * @return the algorithm function associated with the given {@link - * TokenAlgorithm} + * @param algorithm the {@link TokenAlgorithm} for which the algorithm function is required + * @return the algorithm function associated with the given {@link TokenAlgorithm} */ Algo getAlgorithm(TokenAlgorithm algorithm); + List ECDSA_ALGORITHMS = + List.of(TokenAlgorithm.ES256, TokenAlgorithm.ES384, TokenAlgorithm.ES512); + + List HMAC_ALGORITHMS = + List.of(TokenAlgorithm.HS256, TokenAlgorithm.HS384, TokenAlgorithm.HS512); + } diff --git a/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/PredefinedKeys.java b/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/PredefinedKeys.java index 0a9bdb5..c531d56 100644 --- a/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/PredefinedKeys.java +++ b/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/PredefinedKeys.java @@ -35,13 +35,14 @@ import java.util.List; *

  • {@link #JWT_ID}: Represents the "jti" (JWT ID) claim.
  • * *

    - * The class also contains a list of all the standard claim constants, accessible via the {@link #KEYS} field. This - * list can be useful for iterating through all the standard claims or checking for the presence of specific claims. + * The class also contains a list of all the standard claim constants, accessible via the {@link + * #KEYS} field. This list can be useful for iterating through all the standard claims or checking + * for the presence of specific claims. *

    - * Note: This class is final and cannot be instantiated. It only serves as a utility class to hold the standard JWT - * claim constants. + * Note: This class is final and cannot be instantiated. It only serves as a utility class to hold + * the standard JWT claim constants. * - * @author Zihlu Wang + * @author zihluwang * @version 1.1.0 * @since 1.0.0 */ @@ -85,7 +86,8 @@ public final class PredefinedKeys { /** * List containing all the standard JWT claim constants. */ - public static final List KEYS = List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID); + public static final List KEYS = + List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID); /** * Private constructor will protect this class from being instantiated. diff --git a/simple-jwt-jjwt/src/main/java/com/onixbyte/simplejwt/jjwt/JjwtTokenResolver.java b/simple-jwt-jjwt/src/main/java/com/onixbyte/simplejwt/jjwt/JjwtTokenResolver.java index 3607631..dc63840 100644 --- a/simple-jwt-jjwt/src/main/java/com/onixbyte/simplejwt/jjwt/JjwtTokenResolver.java +++ b/simple-jwt-jjwt/src/main/java/com/onixbyte/simplejwt/jjwt/JjwtTokenResolver.java @@ -112,9 +112,6 @@ public class JjwtTokenResolver implements TokenResolver> { } if (secret.length() < 32) { - log.error(""" - The provided secret which owns {} characters is too weak. Please replace it with a stronger one.""", - secret.length()); throw new WeakSecretException(""" The provided secret which owns %s characters is too weak. Please replace it with a stronger one.""" .formatted(secret.length())); @@ -166,9 +163,6 @@ public class JjwtTokenResolver implements TokenResolver> { } if (secret.length() < 32) { - log.error( - "The provided secret which owns {} characters is too weak. Please replace it with a stronger one.", - secret.length()); throw new WeakSecretException( "The provided secret which owns %s characters is too weak. Please replace it with a stronger one." .formatted(secret.length())); diff --git a/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/AuthzeroTokenResolverAutoConfiguration.java b/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/AuthzeroTokenResolverAutoConfiguration.java index 03dad48..4c93400 100644 --- a/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/AuthzeroTokenResolverAutoConfiguration.java +++ b/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/AuthzeroTokenResolverAutoConfiguration.java @@ -23,6 +23,7 @@ import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver; import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties; import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.databind.ObjectMapper; +import com.onixbyte.simplejwt.config.TokenResolverConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -35,25 +36,23 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean; /** - * {@code AuthzeroTokenResolverAutoConfiguration} is responsible for - * automatically configuring the Simple JWT library with - * {@code com.auth0:java-jwt} when used in a Spring Boot application. It - * provides default settings and configurations to ensure that the library - * works smoothly without requiring manual configuration. + * {@code AuthzeroTokenResolverAutoConfiguration} is responsible for automatically configuring the + * Simple JWT library with + * {@code com.auth0:java-jwt} when used in a Spring Boot application. It provides default settings + * and configurations to ensure that the library works smoothly without requiring + * manual configuration. *

    - * This autoconfiguration class sets up the necessary beans and components - * required for JWT generation and validation. It automatically creates and - * configures the {@link AuthzeroTokenResolver} bean based on the available - * options and properties. + * This autoconfiguration class sets up the necessary beans and components required for JWT + * generation and validation. It automatically creates and configures the + * {@link AuthzeroTokenResolver} bean based on the available options and properties. *

    - * Developers using the Simple JWT library with Spring Boot do not need to - * explicitly configure the library, as the autoconfiguration takes care of - * setting up the necessary components and configurations automatically. - * However, developers still have the flexibility to customise the behavior of - * the library by providing their own configurations and properties. + * Developers using the Simple JWT library with Spring Boot do not need to explicitly configure the + * library, as the autoconfiguration takes care of setting up the necessary components and + * configurations automatically. However, developers still have the flexibility to customise the + * behavior of the library by providing their own configurations and properties. * - * @author Zihlu Wang - * @version 1.0.0 + * @author zihluwang + * @version 1.6.0 * @since 1.0.0 */ @Slf4j @@ -70,11 +69,13 @@ public class AuthzeroTokenResolverAutoConfiguration { * provided SimpleJwtProperties. * * @param simpleJwtProperties a {@link SimpleJwtProperties} instance - * @param jtiCreator a creator to create ids for JSON Web Token - * @param objectMapper jackson JSON Handler + * @param jtiCreator a creator to create ids for JSON Web Token + * @param objectMapper jackson JSON Handler */ @Autowired - public AuthzeroTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties, @Qualifier("jtiCreator") GuidCreator jtiCreator, ObjectMapper objectMapper) { + public AuthzeroTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties, + @Qualifier("jtiCreator") GuidCreator jtiCreator, + ObjectMapper objectMapper) { this.jtiCreator = jtiCreator; this.simpleJwtProperties = simpleJwtProperties; this.objectMapper = objectMapper; @@ -90,13 +91,25 @@ public class AuthzeroTokenResolverAutoConfiguration { */ @Bean public TokenResolver tokenResolver() { - return new AuthzeroTokenResolver( - jtiCreator, - simpleJwtProperties.algorithm(), - simpleJwtProperties.issuer(), - simpleJwtProperties.secret(), - objectMapper - ); + if (TokenResolverConfig.HMAC_ALGORITHMS.contains(simpleJwtProperties.algorithm())) { + return new AuthzeroTokenResolver( + jtiCreator, + simpleJwtProperties.algorithm(), + simpleJwtProperties.issuer(), + simpleJwtProperties.secret(), + "", + objectMapper + ); + } else { + return new AuthzeroTokenResolver( + jtiCreator, + simpleJwtProperties.algorithm(), + simpleJwtProperties.issuer(), + simpleJwtProperties.getPrivateKey(), + simpleJwtProperties.getPublicKey(), + objectMapper + ); + } } private final GuidCreator jtiCreator; diff --git a/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/properties/SimpleJwtProperties.java b/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/properties/SimpleJwtProperties.java index 47d121e..cf1b90d 100644 --- a/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/properties/SimpleJwtProperties.java +++ b/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/properties/SimpleJwtProperties.java @@ -71,6 +71,13 @@ public class SimpleJwtProperties { */ private String secret = SecretCreator.createSecret(32, true, true, true); + /** + * The private key of + */ + private String privateKey; + + private String publicKey; + /** * Returns the JWT algorithm configured in the properties. *