refactor: moved MapUtil implemented by reflect API to another package
This commit is contained in:
@@ -34,8 +34,7 @@ import java.util.Objects;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AesUtil} can help you encrypt and decrypt data with specified secret
|
* {@link AesUtil} can help you encrypt and decrypt data with specified secret by AES algorithm.
|
||||||
* by AES algorithm.
|
|
||||||
*
|
*
|
||||||
* @author hubin@baomidou
|
* @author hubin@baomidou
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
@@ -81,8 +80,9 @@ public final class AesUtil {
|
|||||||
var cipher = Cipher.getInstance(AES_CBC_CIPHER);
|
var cipher = Cipher.getInstance(AES_CBC_CIPHER);
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
|
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
|
||||||
return cipher.doFinal(data);
|
return cipher.doFinal(data);
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedOperationException |
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException |
|
||||||
InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException |
|
UnsupportedOperationException | InvalidKeyException |
|
||||||
|
InvalidAlgorithmParameterException | IllegalBlockSizeException |
|
||||||
BadPaddingException exception) {
|
BadPaddingException exception) {
|
||||||
log.error(exception.getMessage());
|
log.error(exception.getMessage());
|
||||||
for (var stackTraceElement : exception.getStackTrace()) {
|
for (var stackTraceElement : exception.getStackTrace()) {
|
||||||
@@ -100,7 +100,8 @@ public final class AesUtil {
|
|||||||
* @return the encryption result or {@code null} if encryption failed
|
* @return the encryption result or {@code null} if encryption failed
|
||||||
*/
|
*/
|
||||||
public static String encrypt(String data, String secret) {
|
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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import java.util.Objects;
|
|||||||
* provided. It is recommended to specify the charset explicitly to ensure consistent
|
* provided. It is recommended to specify the charset explicitly to ensure consistent
|
||||||
* encoding and decoding.
|
* encoding and decoding.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -293,7 +293,9 @@ public final class ChainedCalcUtil {
|
|||||||
* or null if not applicable
|
* or null if not applicable
|
||||||
* @return a ChainedCalcUtil instance with the updated value
|
* @return a ChainedCalcUtil instance with the updated value
|
||||||
*/
|
*/
|
||||||
private ChainedCalcUtil operator(BiFunction<BigDecimal, BigDecimal, BigDecimal> operator, Object other, Integer beforeOperateScale) {
|
private ChainedCalcUtil operator(BiFunction<BigDecimal, BigDecimal, BigDecimal> operator,
|
||||||
|
Object other,
|
||||||
|
Integer beforeOperateScale) {
|
||||||
return baseOperator((otherValue) ->
|
return baseOperator((otherValue) ->
|
||||||
operator.apply(this.value, otherValue),
|
operator.apply(this.value, otherValue),
|
||||||
other,
|
other,
|
||||||
@@ -311,7 +313,8 @@ public final class ChainedCalcUtil {
|
|||||||
* @return a ChainedCalcUtil instance with the updated value
|
* @return a ChainedCalcUtil instance with the updated value
|
||||||
*/
|
*/
|
||||||
private synchronized ChainedCalcUtil baseOperator(Function<BigDecimal, BigDecimal> operatorFunction,
|
private synchronized ChainedCalcUtil baseOperator(Function<BigDecimal, BigDecimal> operatorFunction,
|
||||||
Object anotherValue, Integer beforeOperateScale) {
|
Object anotherValue,
|
||||||
|
Integer beforeOperateScale) {
|
||||||
if (Objects.isNull(anotherValue)) {
|
if (Objects.isNull(anotherValue)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ import java.util.Optional;
|
|||||||
* for data integrity checks and password storage, but they should not be used for
|
* for data integrity checks and password storage, but they should not be used for
|
||||||
* encryption purposes.
|
* encryption purposes.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @see java.security.MessageDigest
|
* @see java.security.MessageDigest
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
|
|||||||
@@ -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.
|
* Note: Since version 1.4.2, this util class removed reflection API and transferred to a safer API.
|
||||||
* Please see documentation for more information.
|
* Please see documentation for more information.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.4.2
|
* @version 1.4.2
|
||||||
* @see com.onixbyte.devkit.utils.unsafe.ReflectMapUtil
|
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ public class KeyLoader {
|
|||||||
*
|
*
|
||||||
* @param pemKeyText pem-formatted key text
|
* @param pemKeyText pem-formatted key text
|
||||||
* @return loaded private key
|
* @return loaded private key
|
||||||
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance, or EC Key Factory is
|
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
|
||||||
* not loaded, or key spec is invalid
|
* or EC Key Factory is not loaded, or key spec is invalid
|
||||||
*/
|
*/
|
||||||
public ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) {
|
public static ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) {
|
||||||
try {
|
try {
|
||||||
var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
|
var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
|
||||||
var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
|
var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
|
||||||
@@ -76,10 +76,10 @@ public class KeyLoader {
|
|||||||
*
|
*
|
||||||
* @param pemKeyText pem-formatted key text
|
* @param pemKeyText pem-formatted key text
|
||||||
* @return loaded private key
|
* @return loaded private key
|
||||||
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance, or EC Key Factory is
|
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
|
||||||
* not loaded, or key spec is invalid
|
* or EC Key Factory is not loaded, or key spec is invalid
|
||||||
*/
|
*/
|
||||||
public ECPublicKey loadEcdsaPublicKey(String pemKeyText) {
|
public static ECPublicKey loadEcdsaPublicKey(String pemKeyText) {
|
||||||
try {
|
try {
|
||||||
var keyBytes = Base64.getDecoder().decode(pemKeyText);
|
var keyBytes = Base64.getDecoder().decode(pemKeyText);
|
||||||
var spec = new X509EncodedKeySpec(keyBytes);
|
var spec = new X509EncodedKeySpec(keyBytes);
|
||||||
|
|||||||
+40
-1
@@ -17,24 +17,63 @@
|
|||||||
|
|
||||||
package com.onixbyte.security.exception;
|
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 {
|
public class KeyLoadingException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code KeyLoadingException} without a specific message or cause.
|
||||||
|
*/
|
||||||
public KeyLoadingException() {
|
public KeyLoadingException() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code KeyLoadingException} with the specified detail message.
|
||||||
|
*
|
||||||
|
* @param message the detail message
|
||||||
|
*/
|
||||||
public KeyLoadingException(String message) {
|
public KeyLoadingException(String message) {
|
||||||
super(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) {
|
public KeyLoadingException(String message, Throwable cause) {
|
||||||
super(message, 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) {
|
public KeyLoadingException(Throwable cause) {
|
||||||
super(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);
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -53,7 +53,7 @@ public final class ReflectMapUtil {
|
|||||||
var declaredFields = obj.getClass().getDeclaredFields();
|
var declaredFields = obj.getClass().getDeclaredFields();
|
||||||
for (var field : declaredFields) {
|
for (var field : declaredFields) {
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
Object result = field.get(obj);
|
var result = field.get(obj);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
map.put(field.getName(), result);
|
map.put(field.getName(), result);
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,8 @@ public final class ReflectMapUtil {
|
|||||||
* @throws IllegalAccessException if an error occurs while accessing the field
|
* @throws IllegalAccessException if an error occurs while accessing the field
|
||||||
* @throws NoSuchMethodException if the specific setter is not present
|
* @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 objectClass = obj.getClass();
|
||||||
var methodName = getMethodName("set", fieldName);
|
var methodName = getMethodName("set", fieldName);
|
||||||
var method = objectClass.getDeclaredMethod(methodName, fieldValue.getClass());
|
var method = objectClass.getDeclaredMethod(methodName, fieldValue.getClass());
|
||||||
@@ -33,6 +33,7 @@ version = buildVersion
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":devkit-utils"))
|
implementation(project(":devkit-utils"))
|
||||||
implementation(project(":guid"))
|
implementation(project(":guid"))
|
||||||
|
implementation(project(":key-pair-loader"))
|
||||||
implementation(project(":simple-jwt-facade"))
|
implementation(project(":simple-jwt-facade"))
|
||||||
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
||||||
implementation("com.auth0:java-jwt:$javaJwtVersion")
|
implementation("com.auth0:java-jwt:$javaJwtVersion")
|
||||||
|
|||||||
+57
-45
@@ -25,6 +25,7 @@ import com.onixbyte.simplejwt.TokenResolver;
|
|||||||
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
|
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
|
||||||
import com.onixbyte.simplejwt.annotations.TokenEnum;
|
import com.onixbyte.simplejwt.annotations.TokenEnum;
|
||||||
import com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
|
import com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
|
||||||
|
import com.onixbyte.simplejwt.config.TokenResolverConfig;
|
||||||
import com.onixbyte.simplejwt.constants.PredefinedKeys;
|
import com.onixbyte.simplejwt.constants.PredefinedKeys;
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
||||||
import com.auth0.jwt.JWT;
|
import com.auth0.jwt.JWT;
|
||||||
@@ -45,15 +46,13 @@ import java.time.ZoneId;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code AuthzeroTokenResolver} class is an implementation of the {@link
|
* The {@code AuthzeroTokenResolver} class is an implementation of the {@link TokenResolver}
|
||||||
* TokenResolver} interface. It uses the {@code
|
* interface. It uses the {@code com.auth0:java-jwt} library to handle JSON Web Token (JWT)
|
||||||
* com.auth0:java-jwt} library to handle JSON Web Token (JWT) resolution. This
|
* resolution. This resolver provides functionality to create, extract, verify, and renew JWT
|
||||||
* resolver provides functionality to create, extract, verify, and renew JWT
|
|
||||||
* tokens using various algorithms and custom payload data.
|
* tokens using various algorithms and custom payload data.
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Usage:</b>
|
* <b>Usage:</b>
|
||||||
* To use the {@code AuthzeroTokenResolver}, first, create an instance of this
|
* To use the {@code AuthzeroTokenResolver}, first, create an instance of this class:
|
||||||
* class:
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* TokenResolver<DecodedJWT> tokenResolver =
|
* TokenResolver<DecodedJWT> tokenResolver =
|
||||||
* new AuthzeroTokenResolver(TokenAlgorithm.HS256,
|
* new AuthzeroTokenResolver(TokenAlgorithm.HS256,
|
||||||
@@ -62,8 +61,7 @@ import java.util.*;
|
|||||||
* "Token Secret");
|
* "Token Secret");
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* <p>
|
* <p>
|
||||||
* Then, you can utilize the various methods provided by this resolver to
|
* Then, you can utilize the various methods provided by this resolver to handle JWT tokens:
|
||||||
* handle JWT tokens:
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* // Creating a new JWT token
|
* // Creating a new JWT token
|
||||||
* String token =
|
* String token =
|
||||||
@@ -82,10 +80,9 @@ import java.util.*;
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Note:</b>
|
* <b>Note:</b>
|
||||||
* It is essential to configure the appropriate algorithms, secret, and issuer
|
* It is essential to configure the appropriate algorithms, secret, and issuer according to your
|
||||||
* according to your specific use case when using this resolver.
|
* specific use case when using this resolver. Additionally, ensure that the
|
||||||
* Additionally, ensure that the {@code com.auth0:java-jwt} library is
|
* {@code com.auth0:java-jwt} library is correctly configured in your project's dependencies.
|
||||||
* correctly configured in your project's dependencies.
|
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author Zihlu Wang
|
||||||
* @version 1.1.1
|
* @version 1.1.1
|
||||||
@@ -100,61 +97,77 @@ import java.util.*;
|
|||||||
public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@code AuthzeroTokenResolver} with the
|
* Creates a new instance of {@code AuthzeroTokenResolver} with the provided configurations.
|
||||||
* provided configurations.
|
|
||||||
*
|
*
|
||||||
* @param jtiCreator the {@link GuidCreator} used for generating unique
|
* @param jtiCreator the {@link GuidCreator} used for generating unique identifiers for "jti"
|
||||||
* identifiers for "jti" claim in JWT tokens
|
* claim in JWT tokens
|
||||||
* @param algorithm the algorithm used for signing and verifying JWT
|
* @param algorithm the algorithm used for signing and verifying JWT tokens
|
||||||
* tokens
|
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
* @param issuer the issuer claim value to be included in JWT tokens
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
* @param privateKey the secret used for HMAC-based algorithms (HS256, HS384, HS512) for
|
||||||
* HS384, HS512) for token signing and verification
|
* 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
|
* @param objectMapper JSON handler
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) {
|
public AuthzeroTokenResolver(GuidCreator<?> jtiCreator,
|
||||||
if (secret == null || secret.isBlank()) {
|
TokenAlgorithm algorithm,
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
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) {
|
if (privateKey.length() < 32) {
|
||||||
log.warn("The provided secret which owns {} characters is too weak. Please consider replacing it with a stronger one.", secret.length());
|
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.jtiCreator = jtiCreator;
|
||||||
this.algorithm = config
|
this.algorithm = config
|
||||||
.getAlgorithm(algorithm)
|
.getAlgorithm(algorithm)
|
||||||
.apply(secret);
|
.apply(privateKey, publicKey);
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
this.verifier = JWT.require(this.algorithm).build();
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link AuthzeroTokenResolver} with the
|
* Creates a new instance of {@link AuthzeroTokenResolver} with the provided configurations
|
||||||
* provided configurations and a simple UUID GuidCreator.
|
* and a simple UUID GuidCreator.
|
||||||
*
|
*
|
||||||
* @param algorithm the algorithm used for signing and verifying 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 issuer the issuer claim value to be included in JWT tokens
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
* @param privateKey the secret used for HMAC-based algorithms (HS256, HS384, HS512) for
|
||||||
* HS384, HS512) for token signing and verification
|
* 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
|
* @param objectMapper Jackson Databind JSON Handler
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) {
|
public AuthzeroTokenResolver(TokenAlgorithm algorithm,
|
||||||
this(UUID::randomUUID, algorithm, issuer, secret, objectMapper);
|
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
|
* Creates a new instance of {@link AuthzeroTokenResolver} with the provided configurations
|
||||||
* provided configurations and a simple UUID GuidCreator.
|
* and a simple UUID GuidCreator.
|
||||||
*
|
*
|
||||||
* @param algorithm the algorithm used for signing and verifying 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 issuer the issuer claim value to be included in JWT tokens
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
* @param privateKey the secret used for HMAC-based algorithms (HS256, HS384, HS512) for
|
||||||
* HS384, HS512) for token signing and verification
|
* 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) {
|
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String privateKey, String publicKey) {
|
||||||
this(UUID::randomUUID, algorithm, issuer, secret, new ObjectMapper());
|
this(UUID::randomUUID, algorithm, issuer, privateKey, publicKey, new ObjectMapper());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,11 +176,10 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
* UUID GuidCreator.
|
* UUID GuidCreator.
|
||||||
*
|
*
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
* @param issuer the issuer claim value to be included in JWT tokens
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
* @param secret the secret used for HS256 algorithms for token signing and verification
|
||||||
* HS384, HS512) for token signing and verification
|
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(String issuer, String secret) {
|
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<DecodedJWT> {
|
|||||||
this.jtiCreator = UUID::randomUUID;
|
this.jtiCreator = UUID::randomUUID;
|
||||||
this.algorithm = config
|
this.algorithm = config
|
||||||
.getAlgorithm(TokenAlgorithm.HS256)
|
.getAlgorithm(TokenAlgorithm.HS256)
|
||||||
.apply(secret);
|
.apply(secret, "");
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
this.verifier = JWT.require(this.algorithm).build();
|
||||||
this.objectMapper = new ObjectMapper();
|
this.objectMapper = new ObjectMapper();
|
||||||
|
|||||||
+19
-23
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.onixbyte.simplejwt.authzero.config;
|
package com.onixbyte.simplejwt.authzero.config;
|
||||||
|
|
||||||
|
import com.onixbyte.security.KeyLoader;
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
import com.onixbyte.simplejwt.TokenResolver;
|
||||||
import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
|
import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
|
||||||
import com.onixbyte.simplejwt.config.TokenResolverConfig;
|
import com.onixbyte.simplejwt.config.TokenResolverConfig;
|
||||||
@@ -30,6 +31,7 @@ import java.security.interfaces.ECPrivateKey;
|
|||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +60,7 @@ import java.util.function.Function;
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
public final class AuthzeroTokenResolverConfig
|
public final class AuthzeroTokenResolverConfig
|
||||||
implements TokenResolverConfig<Function<String, Algorithm>> {
|
implements TokenResolverConfig<BiFunction<String, String, Algorithm>> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the instance of {@code AuthzeroTokenResolverConfig}.
|
* Gets the instance of {@code AuthzeroTokenResolverConfig}.
|
||||||
@@ -92,7 +94,7 @@ public final class AuthzeroTokenResolverConfig
|
|||||||
* this implementation
|
* this implementation
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Function<String, Algorithm> getAlgorithm(TokenAlgorithm algorithm) {
|
public BiFunction<String, String, Algorithm> getAlgorithm(TokenAlgorithm algorithm) {
|
||||||
return Optional.of(SUPPORTED_ALGORITHMS).map((entry) -> entry.get(algorithm))
|
return Optional.of(SUPPORTED_ALGORITHMS).map((entry) -> entry.get(algorithm))
|
||||||
.orElseThrow(() -> new UnsupportedAlgorithmException("The specified algorithm is not supported yet."));
|
.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
|
* specific algorithms. The mapping is used to provide proper algorithm
|
||||||
* resolution and processing within the {@link AuthzeroTokenResolver}.
|
* resolution and processing within the {@link AuthzeroTokenResolver}.
|
||||||
*/
|
*/
|
||||||
private static final Map<TokenAlgorithm, Function<String, Algorithm>> SUPPORTED_ALGORITHMS = new HashMap<>() {{
|
private static final
|
||||||
put(TokenAlgorithm.HS256, Algorithm::HMAC256);
|
Map<TokenAlgorithm, BiFunction<String, String, Algorithm>> SUPPORTED_ALGORITHMS =
|
||||||
put(TokenAlgorithm.HS384, Algorithm::HMAC384);
|
new HashMap<>() {{
|
||||||
put(TokenAlgorithm.HS512, Algorithm::HMAC512);
|
put(TokenAlgorithm.HS256, (String secret, String ignoredValue) ->
|
||||||
put(TokenAlgorithm.ES256, (String privateKey) -> {
|
Algorithm.HMAC256(secret));
|
||||||
try {
|
put(TokenAlgorithm.HS384, (String secret, String ignoredValue) ->
|
||||||
var keyBytes = Base64.getDecoder().decode(privateKey);
|
Algorithm.HMAC384(secret));
|
||||||
var spec = new PKCS8EncodedKeySpec(keyBytes);
|
put(TokenAlgorithm.HS512, (String secret, String ignoredValue) ->
|
||||||
var kf = KeyFactory.getInstance("EC");
|
Algorithm.HMAC512(secret));
|
||||||
var key = kf.generatePrivate(spec);
|
put(TokenAlgorithm.ES256, (String privateKey, String publicKey) ->
|
||||||
if (key instanceof ECPrivateKey pk) {
|
Algorithm.ECDSA256(KeyLoader.loadEcdsaPrivateKey(privateKey)));
|
||||||
return Algorithm.ECDSA256(pk);
|
put(TokenAlgorithm.ES384, (String privateKey, String publicKey) ->
|
||||||
} else {
|
Algorithm.ECDSA256(KeyLoader.loadEcdsaPrivateKey(privateKey)));
|
||||||
throw new RuntimeException("Type error!");
|
put(TokenAlgorithm.ES512, (String privateKey, String publicKey) ->
|
||||||
}
|
Algorithm.ECDSA256(KeyLoader.loadEcdsaPrivateKey(privateKey)));
|
||||||
} catch (NoSuchAlgorithmException ignored) {
|
|
||||||
} catch (InvalidKeySpecException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -20,6 +20,8 @@ package com.onixbyte.simplejwt.config;
|
|||||||
import com.onixbyte.simplejwt.TokenResolver;
|
import com.onixbyte.simplejwt.TokenResolver;
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code TokenResolverConfig} provides a mechanism to configure an
|
* The {@code TokenResolverConfig} provides a mechanism to configure an
|
||||||
* implementation of {@link TokenResolver} with algorithm functions.
|
* implementation of {@link TokenResolver} with algorithm functions.
|
||||||
@@ -49,11 +51,15 @@ public interface TokenResolverConfig<Algo> {
|
|||||||
* implementation that can be used by the {@link TokenResolver} to handle
|
* implementation that can be used by the {@link TokenResolver} to handle
|
||||||
* the specific algorithm.
|
* the specific algorithm.
|
||||||
*
|
*
|
||||||
* @param algorithm the {@link TokenAlgorithm} for which the algorithm
|
* @param algorithm the {@link TokenAlgorithm} for which the algorithm function is required
|
||||||
* function is required
|
* @return the algorithm function associated with the given {@link TokenAlgorithm}
|
||||||
* @return the algorithm function associated with the given {@link
|
|
||||||
* TokenAlgorithm}
|
|
||||||
*/
|
*/
|
||||||
Algo getAlgorithm(TokenAlgorithm algorithm);
|
Algo getAlgorithm(TokenAlgorithm algorithm);
|
||||||
|
|
||||||
|
List<TokenAlgorithm> ECDSA_ALGORITHMS =
|
||||||
|
List.of(TokenAlgorithm.ES256, TokenAlgorithm.ES384, TokenAlgorithm.ES512);
|
||||||
|
|
||||||
|
List<TokenAlgorithm> HMAC_ALGORITHMS =
|
||||||
|
List.of(TokenAlgorithm.HS256, TokenAlgorithm.HS384, TokenAlgorithm.HS512);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -35,13 +35,14 @@ import java.util.List;
|
|||||||
* <li>{@link #JWT_ID}: Represents the "jti" (JWT ID) claim.</li>
|
* <li>{@link #JWT_ID}: Represents the "jti" (JWT ID) claim.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The class also contains a list of all the standard claim constants, accessible via the {@link #KEYS} field. This
|
* The class also contains a list of all the standard claim constants, accessible via the {@link
|
||||||
* list can be useful for iterating through all the standard claims or checking for the presence of specific claims.
|
* #KEYS} field. This list can be useful for iterating through all the standard claims or checking
|
||||||
|
* for the presence of specific claims.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: This class is final and cannot be instantiated. It only serves as a utility class to hold the standard JWT
|
* Note: This class is final and cannot be instantiated. It only serves as a utility class to hold
|
||||||
* claim constants.
|
* the standard JWT claim constants.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +86,8 @@ public final class PredefinedKeys {
|
|||||||
/**
|
/**
|
||||||
* List containing all the standard JWT claim constants.
|
* List containing all the standard JWT claim constants.
|
||||||
*/
|
*/
|
||||||
public static final List<String> KEYS = List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID);
|
public static final List<String> KEYS =
|
||||||
|
List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor will protect this class from being instantiated.
|
* Private constructor will protect this class from being instantiated.
|
||||||
|
|||||||
@@ -112,9 +112,6 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (secret.length() < 32) {
|
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("""
|
throw new WeakSecretException("""
|
||||||
The provided secret which owns %s characters is too weak. Please replace it with a stronger one."""
|
The provided secret which owns %s characters is too weak. Please replace it with a stronger one."""
|
||||||
.formatted(secret.length()));
|
.formatted(secret.length()));
|
||||||
@@ -166,9 +163,6 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (secret.length() < 32) {
|
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(
|
throw new WeakSecretException(
|
||||||
"The provided secret which owns %s characters is too weak. Please replace it with a stronger one."
|
"The provided secret which owns %s characters is too weak. Please replace it with a stronger one."
|
||||||
.formatted(secret.length()));
|
.formatted(secret.length()));
|
||||||
|
|||||||
+39
-26
@@ -23,6 +23,7 @@ import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
|
|||||||
import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
||||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.onixbyte.simplejwt.config.TokenResolverConfig;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@@ -35,25 +36,23 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code AuthzeroTokenResolverAutoConfiguration} is responsible for
|
* {@code AuthzeroTokenResolverAutoConfiguration} is responsible for automatically configuring the
|
||||||
* automatically configuring the Simple JWT library with
|
* Simple JWT library with
|
||||||
* {@code com.auth0:java-jwt} when used in a Spring Boot application. It
|
* {@code com.auth0:java-jwt} when used in a Spring Boot application. It provides default settings
|
||||||
* provides default settings and configurations to ensure that the library
|
* and configurations to ensure that the library works smoothly without requiring
|
||||||
* works smoothly without requiring manual configuration.
|
* manual configuration.
|
||||||
* <p>
|
* <p>
|
||||||
* This autoconfiguration class sets up the necessary beans and components
|
* This autoconfiguration class sets up the necessary beans and components required for JWT
|
||||||
* required for JWT generation and validation. It automatically creates and
|
* generation and validation. It automatically creates and configures the
|
||||||
* configures the {@link AuthzeroTokenResolver} bean based on the available
|
* {@link AuthzeroTokenResolver} bean based on the available options and properties.
|
||||||
* options and properties.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Developers using the Simple JWT library with Spring Boot do not need to
|
* Developers using the Simple JWT library with Spring Boot do not need to explicitly configure the
|
||||||
* explicitly configure the library, as the autoconfiguration takes care of
|
* library, as the autoconfiguration takes care of setting up the necessary components and
|
||||||
* setting up the necessary components and configurations automatically.
|
* configurations automatically. However, developers still have the flexibility to customise the
|
||||||
* However, developers still have the flexibility to customise the behavior of
|
* behavior of the library by providing their own configurations and properties.
|
||||||
* the library by providing their own configurations and properties.
|
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.0.0
|
* @version 1.6.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -70,11 +69,13 @@ public class AuthzeroTokenResolverAutoConfiguration {
|
|||||||
* provided SimpleJwtProperties.
|
* provided SimpleJwtProperties.
|
||||||
*
|
*
|
||||||
* @param simpleJwtProperties a {@link SimpleJwtProperties} instance
|
* @param simpleJwtProperties a {@link SimpleJwtProperties} instance
|
||||||
* @param jtiCreator a creator to create ids for JSON Web Token
|
* @param jtiCreator a creator to create ids for JSON Web Token
|
||||||
* @param objectMapper jackson JSON Handler
|
* @param objectMapper jackson JSON Handler
|
||||||
*/
|
*/
|
||||||
@Autowired
|
@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.jtiCreator = jtiCreator;
|
||||||
this.simpleJwtProperties = simpleJwtProperties;
|
this.simpleJwtProperties = simpleJwtProperties;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
@@ -90,13 +91,25 @@ public class AuthzeroTokenResolverAutoConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public TokenResolver<DecodedJWT> tokenResolver() {
|
public TokenResolver<DecodedJWT> tokenResolver() {
|
||||||
return new AuthzeroTokenResolver(
|
if (TokenResolverConfig.HMAC_ALGORITHMS.contains(simpleJwtProperties.algorithm())) {
|
||||||
jtiCreator,
|
return new AuthzeroTokenResolver(
|
||||||
simpleJwtProperties.algorithm(),
|
jtiCreator,
|
||||||
simpleJwtProperties.issuer(),
|
simpleJwtProperties.algorithm(),
|
||||||
simpleJwtProperties.secret(),
|
simpleJwtProperties.issuer(),
|
||||||
objectMapper
|
simpleJwtProperties.secret(),
|
||||||
);
|
"",
|
||||||
|
objectMapper
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new AuthzeroTokenResolver(
|
||||||
|
jtiCreator,
|
||||||
|
simpleJwtProperties.algorithm(),
|
||||||
|
simpleJwtProperties.issuer(),
|
||||||
|
simpleJwtProperties.getPrivateKey(),
|
||||||
|
simpleJwtProperties.getPublicKey(),
|
||||||
|
objectMapper
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final GuidCreator<?> jtiCreator;
|
private final GuidCreator<?> jtiCreator;
|
||||||
|
|||||||
+7
@@ -71,6 +71,13 @@ public class SimpleJwtProperties {
|
|||||||
*/
|
*/
|
||||||
private String secret = SecretCreator.createSecret(32, true, true, true);
|
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.
|
* Returns the JWT algorithm configured in the properties.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user