Merge pull request #25 from OnixByte/development

Migrate group id from cn.org.codecrafters to com.onixbyte
This commit is contained in:
Zihlu Wang
2024-04-26 02:47:46 +08:00
committed by GitHub
67 changed files with 679 additions and 626 deletions
+2 -2
View File
@@ -58,8 +58,8 @@ publishing {
} }
scm { scm {
connection = "scm:git:git://github.com:CodeCraftersCN/JDevKit.git" connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
developerConnection = "scm:git:git://github.com:CodeCraftersCN/JDevKit.git" developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
url = projectGithubUrl url = projectGithubUrl
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.core.exceptions; package com.onixbyte.devkit.core.exceptions;
/** /**
* The {@code NotImplementedException} class is a custom runtime exception * The {@code NotImplementedException} class is a custom runtime exception
@@ -21,4 +21,4 @@
* @author Zihlu Wang * @author Zihlu Wang
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.devkit.core.exceptions; package com.onixbyte.devkit.core.exceptions;
@@ -40,4 +40,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.devkit.core; package com.onixbyte.devkit.core;
@@ -39,4 +39,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.devkit; package com.onixbyte.devkit;
+5 -5
View File
@@ -51,7 +51,7 @@ implementation 'cn.org.codecrafters:devkit-utils:${devkit-utils.version}'
If you are trying to encode a string to Base64 string or decode a Base64 string to normal string, then you can try this: If you are trying to encode a string to Base64 string or decode a Base64 string to normal string, then you can try this:
```java ```java
import cn.org.codecrafters.devkit.utils.Base64Util; import utils.com.onixbyte.devkit.Base64Util;
// To reduce sample codes, let me use the simplified main method that is upcoming in Java 21 // To reduce sample codes, let me use the simplified main method that is upcoming in Java 21
void main(String... args) { void main(String... args) {
@@ -68,7 +68,7 @@ void main(String... args) {
I believe those `if...else...` blocks make you headache, and Java imported lambda since Java 8, why not try to replace those `if...else` with lambda expressions? I believe those `if...else...` blocks make you headache, and Java imported lambda since Java 8, why not try to replace those `if...else` with lambda expressions?
```java ```java
import cn.org.codecrafters.devkit.utils.BranchUtil; import utils.com.onixbyte.devkit.BranchUtil;
void main(String... args) { void main(String... args) {
var a = 1; var a = 1;
@@ -106,7 +106,7 @@ If you have faced high-precision mathematical calculation in Java, you might kno
In Java, we usually do high-precision mathematical calculation with `BigDecimal` which is quite tricky when using it. In Java, we usually do high-precision mathematical calculation with `BigDecimal` which is quite tricky when using it.
```java ```java
import cn.org.codecrafters.devkit.utils.ChainedCalcUtil; import utils.com.onixbyte.devkit.ChainedCalcUtil;
void main(String... args) { void main(String... args) {
// If you are trying to calculate the expression of 1 * 2 / 2 - 3 + 4 // If you are trying to calculate the expression of 1 * 2 / 2 - 3 + 4
@@ -133,7 +133,7 @@ This `HashUtil` supports these following hash or message digest algorithms:
If you want to run a hash calculation to a string, you can use the following codes: If you want to run a hash calculation to a string, you can use the following codes:
```java ```java
import cn.org.codecrafters.devkit.utils.HashUtil; import utils.com.onixbyte.devkit.HashUtil;
void main(String... args) { void main(String... args) {
var plaintext = "This is a plain text"; var plaintext = "This is a plain text";
@@ -156,7 +156,7 @@ Imagine you are developing a website where users can register an account and sto
In order to store the Map in a database, you need to convert the Map to an Object. An Object is a generic data type that can store any type of data. In order to store the Map in a database, you need to convert the Map to an Object. An Object is a generic data type that can store any type of data.
```java ```java
import cn.org.codecrafters.devkit.utils.MapUtil; import utils.com.onixbyte.devkit.MapUtil;
class Data { class Data {
private String name; private String name;
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -44,13 +44,6 @@ import java.util.UUID;
@Slf4j @Slf4j
public final class AesUtil { public final class AesUtil {
private AesUtil() {
}
private static final String AES = "AES";
private static final String AES_CBC_CIPHER = "AES/CBC/PKCS5Padding";
/** /**
* Encrypts the data using the AES algorithm with the given secret. * Encrypts the data using the AES algorithm with the given secret.
* *
@@ -133,4 +126,20 @@ public final class AesUtil {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16); return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
} }
/**
* Private constructor will protect this class from being instantiated.
*/
private AesUtil() {
}
/**
* The algorithm AES.
*/
private static final String AES = "AES";
/**
* The algorithm AES/CBC/PKCS5Padding.
*/
private static final String AES_CBC_CIPHER = "AES/CBC/PKCS5Padding";
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -56,14 +56,6 @@ import java.util.Objects;
*/ */
public final class Base64Util { public final class Base64Util {
private static Base64.Encoder encoder;
private static Base64.Decoder decoder;
private static Base64.Encoder urlEncoder;
private static Base64.Decoder urlDecoder;
/** /**
* Ensure that there is only one Base64 Encoder. * Ensure that there is only one Base64 Encoder.
* *
@@ -210,4 +202,12 @@ public final class Base64Util {
return decodeUrlComponents(value, StandardCharsets.UTF_8); return decodeUrlComponents(value, StandardCharsets.UTF_8);
} }
private static Base64.Encoder encoder;
private static Base64.Decoder decoder;
private static Base64.Encoder urlEncoder;
private static Base64.Decoder urlDecoder;
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
@@ -77,11 +77,6 @@ import java.util.function.Supplier;
*/ */
public final class BranchUtil<T> { public final class BranchUtil<T> {
/**
* The final result of the boolean expression.
*/
private final boolean result;
/** /**
* Create a {@code BranchUtil} instance. * Create a {@code BranchUtil} instance.
* *
@@ -239,4 +234,9 @@ public final class BranchUtil<T> {
handle(ifHandler, null); handle(ifHandler, null);
} }
/**
* The final result of the boolean expression.
*/
private final boolean result;
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
import lombok.Getter; import lombok.Getter;
@@ -95,12 +95,6 @@ import java.util.function.Function;
@Getter @Getter
public final class ChainedCalcUtil { public final class ChainedCalcUtil {
/**
* -- GETTER --
* Returns the current value as a BigDecimal.
*/
private BigDecimal value;
/** /**
* Creates a {@code ChainedCalcUtil} instance with the specified initial * Creates a {@code ChainedCalcUtil} instance with the specified initial
* value. * value.
@@ -359,4 +353,10 @@ public final class ChainedCalcUtil {
return res; return res;
} }
/**
* -- GETTER --
* Returns the current value as a BigDecimal.
*/
private BigDecimal value;
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -69,48 +69,6 @@ import java.util.Optional;
*/ */
public final class HashUtil { public final class HashUtil {
/**
* Private constructor to prevent instantiation
*/
private HashUtil() {
}
/**
* Calculates the hash value of the specified string using the specified
* algorithm and charset.
*
* @param method the hash algorithm to use
* @param value the string to calculate the hash value for
* @param charset the charset to use for encoding the string (default is
* UTF-8 if null)
* @return the hash value as a hexadecimal string, or an empty string if
* the algorithm is not available
* @throws RuntimeException if an unknown algorithm name is provided
* (should not occur under controlled usage)
*/
private static String hash(String method, String value, Charset charset) {
try {
var messageDigest = MessageDigest.getInstance(method);
messageDigest.update(value.getBytes(charset));
var bytes = messageDigest.digest();
var builder = new StringBuilder();
for (var b : bytes) {
var str = Integer.toHexString(b & 0xff);
if (str.length() == 1) {
builder.append(0);
}
builder.append(str);
}
return builder.toString();
} catch (NoSuchAlgorithmException ignored) {
// This should not occur under controlled usage
// Only trusted algorithms are allowed
return "";
}
}
/** /**
* Calculates the MD2 hash value of the specified string using the given * Calculates the MD2 hash value of the specified string using the given
* charset. * charset.
@@ -286,4 +244,46 @@ public final class HashUtil {
return hash("SHA-512", value, StandardCharsets.UTF_8); return hash("SHA-512", value, StandardCharsets.UTF_8);
} }
/**
* Private constructor will protect this class from being instantiated.
*/
private HashUtil() {
}
/**
* Calculates the hash value of the specified string using the specified
* algorithm and charset.
*
* @param method the hash algorithm to use
* @param value the string to calculate the hash value for
* @param charset the charset to use for encoding the string (default is
* UTF-8 if null)
* @return the hash value as a hexadecimal string, or an empty string if
* the algorithm is not available
* @throws RuntimeException if an unknown algorithm name is provided
* (should not occur under controlled usage)
*/
private static String hash(String method, String value, Charset charset) {
try {
var messageDigest = MessageDigest.getInstance(method);
messageDigest.update(value.getBytes(charset));
var bytes = messageDigest.digest();
var builder = new StringBuilder();
for (var b : bytes) {
var str = Integer.toHexString(b & 0xff);
if (str.length() == 1) {
builder.append(0);
}
builder.append(str);
}
return builder.toString();
} catch (NoSuchAlgorithmException ignored) {
// This should not occur under controlled usage
// Only trusted algorithms are allowed
return "";
}
}
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -37,12 +37,6 @@ import java.util.Map;
@Slf4j @Slf4j
public final class MapUtil { public final class MapUtil {
/**
* Private constructor to prevent instantiation of MapUtil.
*/
private MapUtil() {
}
/** /**
* Converts an object to a map by mapping the field names to their * Converts an object to a map by mapping the field names to their
* corresponding values. * corresponding values.
@@ -219,4 +213,10 @@ public final class MapUtil {
return String.valueOf(obj); return String.valueOf(obj);
} }
} }
/**
* Private constructor will protect this class from being instantiated.
*/
private MapUtil() {
}
} }
@@ -25,4 +25,4 @@
* @author Zihlu Wang * @author Zihlu Wang
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.devkit.utils; package com.onixbyte.devkit.utils;
+3 -3
View File
@@ -26,9 +26,9 @@ okhttpVersion=4.12.0
springVersion=6.1.3 springVersion=6.1.3
springBootVersion=3.2.3 springBootVersion=3.2.3
buildGroupId=cn.org.codecrafters buildGroupId=com.onixbyte
buildVersion=1.4.0 buildVersion=1.4.0
projectUrl=https://codecrafters.org.cn/JDevKit projectUrl=https://onixbyte.com/JDevKit
projectGithubUrl=https://github.com/CodeCraftersCN/JDevKit projectGithubUrl=https://github.com/OnixByte/JDevKit
licenseName=The Apache License, Version 2.0 licenseName=The Apache License, Version 2.0
licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.txt licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.txt
Vendored Regular → Executable
View File
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.guid; package com.onixbyte.guid;
/** /**
* The {@code GuidCreator} is a generic interface for generating globally unique * The {@code GuidCreator} is a generic interface for generating globally unique
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.guid.exceptions; package com.onixbyte.guid.exceptions;
/** /**
* The {@code TimingException} class represents an exception that is thrown * The {@code TimingException} class represents an exception that is thrown
@@ -21,7 +21,7 @@
* during the generation or processing of global unique identifiers (GUIDs). * during the generation or processing of global unique identifiers (GUIDs).
* <p> * <p>
* The main exception class in this package is {@link * The main exception class in this package is {@link
* cn.org.codecrafters.guid.exceptions.TimingException}, which is a runtime * com.onixbyte.guid.exceptions.TimingException}, which is a runtime
* exception and serves as the base exception for all other custom exceptions * exception and serves as the base exception for all other custom exceptions
* related to GUID generation. * related to GUID generation.
* <p> * <p>
@@ -38,4 +38,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.guid.exceptions; package com.onixbyte.guid.exceptions;
@@ -15,13 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.guid; package com.onixbyte.guid.impl;
import cn.org.codecrafters.guid.exceptions.TimingException; import com.onixbyte.guid.GuidCreator;
import com.onixbyte.guid.exceptions.TimingException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset;
/** /**
* The {@code SnowflakeGuidCreator} generates unique identifiers using the * The {@code SnowflakeGuidCreator} generates unique identifiers using the
@@ -49,48 +49,6 @@ import java.time.ZoneOffset;
*/ */
public final class SnowflakeGuidCreator implements GuidCreator<Long> { public final class SnowflakeGuidCreator implements GuidCreator<Long> {
/**
* Default custom epoch.
*
* @value 2015-01-01T00:00:00Z
*/
private static final long DEFAULT_CUSTOM_EPOCH = 1_420_070_400_000L;
/**
* The start epoch timestamp to generate IDs from.
*/
private final long startEpoch;
/**
* The number of bits reserved for the worker ID.
*/
private final long workerIdBits = 5L;
/**
* The number of bits reserved for the data centre ID.
*/
private final long dataCentreIdBits = 5L;
/**
* The worker ID assigned to this generator.
*/
private final long workerId;
/**
* The data centre ID assigned to this generator.
*/
private final long dataCentreId;
/**
* The current sequence number.
*/
private long sequence = 0L;
/**
* The timestamp of the last generated ID.
*/
private long lastTimestamp = -1L;
/** /**
* Constructs a SnowflakeGuidGenerator with the default start epoch and * Constructs a SnowflakeGuidGenerator with the default start epoch and
* custom worker ID, data centre ID. * custom worker ID, data centre ID.
@@ -204,5 +162,48 @@ public final class SnowflakeGuidCreator implements GuidCreator<Long> {
private long currentTimestamp() { private long currentTimestamp() {
return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
} }
/**
* Default custom epoch.
*
* @value 2015-01-01T00:00:00Z
*/
private static final long DEFAULT_CUSTOM_EPOCH = 1_420_070_400_000L;
/**
* The start epoch timestamp to generate IDs from.
*/
private final long startEpoch;
/**
* The number of bits reserved for the worker ID.
*/
private final long workerIdBits = 5L;
/**
* The number of bits reserved for the data centre ID.
*/
private final long dataCentreIdBits = 5L;
/**
* The worker ID assigned to this generator.
*/
private final long workerId;
/**
* The data centre ID assigned to this generator.
*/
private final long dataCentreId;
/**
* The current sequence number.
*/
private long sequence = 0L;
/**
* The timestamp of the last generated ID.
*/
private long lastTimestamp = -1L;
} }
@@ -32,4 +32,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.guid; package com.onixbyte.guid;
+4 -4
View File
@@ -48,12 +48,12 @@ implementation 'cn.org.codecrafters:property-guard-spring-boot-starter:${propert
## Usage ## Usage
First, you need a 16-bit-long secret. If you don't have a good way to get a secret, you could consider using our `cn.org.codecrafters.devkit.utils.AesUtil` or `cn.org.codecrafters.simplejwt.SecretCreator` to create a secret. First, you need a 16-bit-long secret. If you don't have a good way to get a secret, you could consider using our `utils.com.onixbyte.devkit.AesUtil` or `com.onixbyte.simplejwt.SecretCreator` to create a secret.
For example: For example:
```java ```java
import cn.org.codecrafters.devkit.utils.AesUtil; import utils.com.onixbyte.devkit.AesUtil;
import cn.org.codecrafters.simplejwt.SecretCreator; import com.onixbyte.simplejwt.SecretCreator;
class GenerateRandomKeySample { class GenerateRandomKeySample {
public static void main(String[] args) { public static void main(String[] args) {
@@ -66,7 +66,7 @@ class GenerateRandomKeySample {
Then, remember this secret and encrypt the configuration properties that are required high security. For example: Then, remember this secret and encrypt the configuration properties that are required high security. For example:
```java ```java
import cn.org.codecrafters.devkit.utils.AesUtil; import utils.com.onixbyte.devkit.AesUtil;
class EncryptSample { class EncryptSample {
public static void main(String[] args) { public static void main(String[] args) {
@@ -15,9 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.propertyguard.autoconfiguration; package com.onixbyte.propertyguard.autoconfiguration;
import cn.org.codecrafters.devkit.utils.AesUtil; import com.onixbyte.devkit.utils.AesUtil;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.boot.env.OriginTrackedMapPropertySource;
@@ -70,8 +70,6 @@ import java.util.Optional;
*/ */
public class PropertyGuard implements EnvironmentPostProcessor { public class PropertyGuard implements EnvironmentPostProcessor {
private final String PREFIX = "pg";
/** /**
* Process the encryption environment variables. * Process the encryption environment variables.
* *
@@ -108,4 +106,6 @@ public class PropertyGuard implements EnvironmentPostProcessor {
} }
} }
} }
private static final String PREFIX = "pg";
} }
@@ -1 +1 @@
org.springframework.boot.env.EnvironmentPostProcessor=cn.org.codecrafters.propertyguard.autoconfiguration.PropertyGuard org.springframework.boot.env.EnvironmentPostProcessor=com.onixbyte.propertyguard.autoconfiguration.PropertyGuard
+1 -1
View File
@@ -50,7 +50,7 @@ implementation 'cn.org.codecrafters:simple-jwt-authzero:${simple-jwt-authzero.ve
## Use the `AuthzeroTokenResolver` ## Use the `AuthzeroTokenResolver`
We have implemented `TokenResolver` to make sure you can add JWT to your Java application as soon as possible. All you need to do is to create an instance of `cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver` and other operations to JWT could follow our instruction in [`simple-jwt-facade`](../simple-jwt-facade/README.md). We have implemented `TokenResolver` to make sure you can add JWT to your Java application as soon as possible. All you need to do is to create an instance of `com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver` and other operations to JWT could follow our instruction in [`simple-jwt-facade`](../simple-jwt-facade/README.md).
## Contact ## Contact
@@ -15,34 +15,29 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.authzero; package com.onixbyte.simplejwt.authzero;
import cn.org.codecrafters.devkit.utils.Base64Util; import com.onixbyte.devkit.utils.Base64Util;
import cn.org.codecrafters.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import cn.org.codecrafters.simplejwt.SecretCreator; import com.onixbyte.simplejwt.SecretCreator;
import cn.org.codecrafters.simplejwt.TokenPayload; import com.onixbyte.simplejwt.TokenPayload;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.annotations.ExcludeFromPayload; import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
import cn.org.codecrafters.simplejwt.annotations.TokenEnum; import com.onixbyte.simplejwt.annotations.TokenEnum;
import cn.org.codecrafters.simplejwt.authzero.config.AuthzeroTokenResolverConfig; import com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
import cn.org.codecrafters.simplejwt.config.TokenResolverConfig; import com.onixbyte.simplejwt.constants.PredefinedKeys;
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier; import com.auth0.jwt.interfaces.JWTVerifier;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -51,7 +46,7 @@ import java.util.*;
/** /**
* The {@code AuthzeroTokenResolver} class is an implementation of the {@link * The {@code AuthzeroTokenResolver} class is an implementation of the {@link
* cn.org.codecrafters.simplejwt.TokenResolver} interface. It uses the {@code * TokenResolver} interface. It uses the {@code
* com.auth0:java-jwt} library to handle JSON Web Token (JWT) resolution. This * com.auth0:java-jwt} library to handle JSON Web Token (JWT) 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.
@@ -104,34 +99,6 @@ import java.util.*;
@Slf4j @Slf4j
public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> { public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
/**
* GuidCreator used for generating unique identifiers for "jti" claim in
* JWT tokens.
*/
private final GuidCreator<?> jtiCreator;
/**
* The algorithm used for signing and verifying JWT tokens.
*/
private final Algorithm algorithm;
/**
* The issuer claim value to be included in JWT tokens.
*/
private final String issuer;
/**
* The JSON Web Token resolver.
*/
private final JWTVerifier verifier;
/**
* Jackson JSON handler.
*/
private final ObjectMapper objectMapper;
private final AuthzeroTokenResolverConfig config = AuthzeroTokenResolverConfig.getInstance();
/** /**
* Creates a new instance of {@code AuthzeroTokenResolver} with the * Creates a new instance of {@code AuthzeroTokenResolver} with the
* provided configurations. * provided configurations.
@@ -150,7 +117,7 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
throw new IllegalArgumentException("A secret is required to build a JSON Web Token."); throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
} }
if (secret.length() <= 32) { 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()); log.warn("The provided secret which owns {} characters is too weak. Please consider replacing it with a stronger one.", secret.length());
} }
@@ -224,104 +191,6 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
log.info("The secret has been set to {}.", secret); log.info("The secret has been set to {}.", secret);
} }
/**
* Builds the basic information of the JSON Web Token (JWT) using the
* provided parameters and adds it to the JWTCreator.Builder.
*
* @param subject the subject claim value to be included in the JWT
* @param audience an array of audience claim values to be included in
* the JWT
* @param expireAfter the duration after which the JWT will expire
* @param builder the JWTCreator.Builder instance to which the basic
* information will be added
*/
private void buildBasicInfo(JWTCreator.Builder builder, Duration expireAfter, String subject, String... audience) {
var now = LocalDateTime.now();
// bind issuer (iss)
builder.withIssuer(issuer);
// bind issued at (iat)
builder.withIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
// bind not before (nbf)
builder.withNotBefore(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
// bind audience (aud)
builder.withAudience(audience);
// bind subject (sub)
builder.withSubject(subject);
// bind expire at (exp)
builder.withExpiresAt(Date.from(now.plus(expireAfter).atZone(ZoneId.systemDefault()).toInstant()));
// bind JWT Id (jti)
builder.withJWTId(jtiCreator.nextId().toString());
}
/**
* Add a claim to a builder.
*
* @param builder the builder to build this JSON Web Token
* @param name the property name
* @param value the property value
*/
private void addClaim(JWTCreator.Builder builder, String name, Object value) {
if (Objects.nonNull(value)) {
if (value instanceof Boolean v) {
builder.withClaim(name, v);
} else if (value instanceof Double v) {
builder.withClaim(name, v);
} else if (value instanceof Float v) {
builder.withClaim(name, v.doubleValue());
} else if (value instanceof Integer v) {
builder.withClaim(name, v);
} else if (value instanceof Long v) {
builder.withClaim(name, v);
} else if (value instanceof String v) {
builder.withClaim(name, v);
} else if (value instanceof Date v) {
builder.withClaim(name, v);
} else if (value instanceof List<?> v) {
builder.withClaim(name, v);
} else {
log.warn("""
Unable to determine the type of field {}, we will handle it as a String.""", name);
builder.withClaim(name, value.toString());
}
} else {
builder.withNullClaim(name);
}
}
/**
* Builds the custom claims of the JSON Web Token (JWT) using the provided
* Map of claims and adds them to the JWTCreator.Builder.
* <p>
* This method is used to add custom claims to the JWT. It takes a Map of
* claims, where each entry represents a custom claim name (key) and its
* corresponding value (value). The custom claims will be added to the JWT
* using the JWTCreator.Builder.
*
* @param claims a Map containing the custom claims to be added to the JWT
* @param builder the JWTCreator.Builder instance to which the custom
* claims will be added
*/
private void buildMapClaims(JWTCreator.Builder builder, Map<String, Object> claims) {
if (Objects.nonNull(claims)) {
for (var e : claims.entrySet()) {
addClaim(builder, e.getKey(), e.getValue());
}
}
}
/**
* Finish creating a token.
* <p>
* This is the final step of create a token, to sign this token.
*
* @param builder the builder to build this JWT
* @return the generated token as a {@code String}
*/
private String buildToken(JWTCreator.Builder builder) {
return builder.sign(algorithm);
}
/** /**
* Creates a new token with the specified expiration duration, subject, and * Creates a new token with the specified expiration duration, subject, and
* audience. * audience.
@@ -552,8 +421,137 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
return renew(oldToken, Duration.ofMinutes(30), payload); return renew(oldToken, Duration.ofMinutes(30), payload);
} }
/**
* Builds the basic information of the JSON Web Token (JWT) using the
* provided parameters and adds it to the JWTCreator.Builder.
*
* @param subject the subject claim value to be included in the JWT
* @param audience an array of audience claim values to be included in
* the JWT
* @param expireAfter the duration after which the JWT will expire
* @param builder the JWTCreator.Builder instance to which the basic
* information will be added
*/
private void buildBasicInfo(JWTCreator.Builder builder, Duration expireAfter, String subject, String... audience) {
var now = LocalDateTime.now();
// bind issuer (iss)
builder.withIssuer(issuer);
// bind issued at (iat)
builder.withIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
// bind not before (nbf)
builder.withNotBefore(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
// bind audience (aud)
builder.withAudience(audience);
// bind subject (sub)
builder.withSubject(subject);
// bind expire at (exp)
builder.withExpiresAt(Date.from(now.plus(expireAfter).atZone(ZoneId.systemDefault()).toInstant()));
// bind JWT Id (jti)
builder.withJWTId(jtiCreator.nextId().toString());
}
/**
* Add a claim to a builder.
*
* @param builder the builder to build this JSON Web Token
* @param name the property name
* @param value the property value
*/
private void addClaim(JWTCreator.Builder builder, String name, Object value) {
if (Objects.nonNull(value)) {
if (value instanceof Boolean v) {
builder.withClaim(name, v);
} else if (value instanceof Double v) {
builder.withClaim(name, v);
} else if (value instanceof Float v) {
builder.withClaim(name, v.doubleValue());
} else if (value instanceof Integer v) {
builder.withClaim(name, v);
} else if (value instanceof Long v) {
builder.withClaim(name, v);
} else if (value instanceof String v) {
builder.withClaim(name, v);
} else if (value instanceof Date v) {
builder.withClaim(name, v);
} else if (value instanceof List<?> v) {
builder.withClaim(name, v);
} else {
log.warn("""
Unable to determine the type of field {}, we will handle it as a String.""", name);
builder.withClaim(name, value.toString());
}
} else {
builder.withNullClaim(name);
}
}
/**
* Builds the custom claims of the JSON Web Token (JWT) using the provided
* Map of claims and adds them to the JWTCreator.Builder.
* <p>
* This method is used to add custom claims to the JWT. It takes a Map of
* claims, where each entry represents a custom claim name (key) and its
* corresponding value (value). The custom claims will be added to the JWT
* using the JWTCreator.Builder.
*
* @param claims a Map containing the custom claims to be added to the JWT
* @param builder the JWTCreator.Builder instance to which the custom
* claims will be added
*/
private void buildMapClaims(JWTCreator.Builder builder, Map<String, Object> claims) {
if (Objects.nonNull(claims)) {
for (var e : claims.entrySet()) {
addClaim(builder, e.getKey(), e.getValue());
}
}
}
/**
* Finish creating a token.
* <p>
* This is the final step of create a token, to sign this token.
*
* @param builder the builder to build this JWT
* @return the generated token as a {@code String}
*/
private String buildToken(JWTCreator.Builder builder) {
return builder.sign(algorithm);
}
/**
* Default type reference for Map.
*/
private static class MapTypeReference extends TypeReference<Map<String, Object>> { private static class MapTypeReference extends TypeReference<Map<String, Object>> {
MapTypeReference() { MapTypeReference() {
} }
} }
/**
* GuidCreator used for generating unique identifiers for "jti" claim in
* JWT tokens.
*/
private final GuidCreator<?> jtiCreator;
/**
* The algorithm used for signing and verifying JWT tokens.
*/
private final Algorithm algorithm;
/**
* The issuer claim value to be included in JWT tokens.
*/
private final String issuer;
/**
* The JSON Web Token resolver.
*/
private final JWTVerifier verifier;
/**
* Jackson JSON handler.
*/
private final ObjectMapper objectMapper;
private final AuthzeroTokenResolverConfig config = AuthzeroTokenResolverConfig.getInstance();
} }
@@ -15,13 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.authzero.config; package com.onixbyte.simplejwt.authzero.config;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver; import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
import cn.org.codecrafters.simplejwt.config.TokenResolverConfig; import com.onixbyte.simplejwt.config.TokenResolverConfig;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
import cn.org.codecrafters.simplejwt.exceptions.UnsupportedAlgorithmException; import com.onixbyte.simplejwt.exceptions.UnsupportedAlgorithmException;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import java.util.HashMap; import java.util.HashMap;
@@ -62,42 +62,6 @@ import java.util.function.Function;
*/ */
public final class AuthzeroTokenResolverConfig implements TokenResolverConfig<Function<String, Algorithm>> { public final class AuthzeroTokenResolverConfig implements TokenResolverConfig<Function<String, Algorithm>> {
/**
* Constructs a new instance of {@code AuthzeroTokenResolverConfig}.
* <p>
* The constructor is set as private to enforce the singleton pattern for
* this configuration class. Instances of
* {@code AuthzeroTokenResolverConfig} should be obtained through the
* {@link #getInstance()} method.
*/
private AuthzeroTokenResolverConfig() {
}
/**
* The singleton instance of {@code AuthzeroTokenResolverConfig}.
* <p>
* This instance is used to ensure that only one instance of
* {@code AuthzeroTokenResolverConfig} is created and shared throughout the
* application. The singleton pattern is implemented to provide centralised
* configuration and avoid redundant object creation.
*/
private static AuthzeroTokenResolverConfig instance;
/**
* The supported algorithms and their corresponding algorithm functions.
* <p>
* This map stores the supported algorithms as keys and their corresponding
* algorithm functions as values. The algorithm functions represent the
* functions used by the {@code com.auth0:java-jwt} library to handle the
* specific algorithms. The mapping is used to provide proper algorithm
* resolution and processing within the {@link AuthzeroTokenResolver}.
*/
private static final Map<TokenAlgorithm, Function<String, Algorithm>> SUPPORTED_ALGORITHMS = new HashMap<>() {{
put(TokenAlgorithm.HS256, Algorithm::HMAC256);
put(TokenAlgorithm.HS384, Algorithm::HMAC384);
put(TokenAlgorithm.HS512, Algorithm::HMAC512);
}};
/** /**
* Gets the instance of {@code AuthzeroTokenResolverConfig}. * Gets the instance of {@code AuthzeroTokenResolverConfig}.
* <p> * <p>
@@ -140,4 +104,40 @@ public final class AuthzeroTokenResolverConfig implements TokenResolverConfig<Fu
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."));
} }
/**
* Constructs a new instance of {@code AuthzeroTokenResolverConfig}.
* <p>
* The constructor is set as private to enforce the singleton pattern for
* this configuration class. Instances of
* {@code AuthzeroTokenResolverConfig} should be obtained through the
* {@link #getInstance()} method.
*/
private AuthzeroTokenResolverConfig() {
}
/**
* The singleton instance of {@code AuthzeroTokenResolverConfig}.
* <p>
* This instance is used to ensure that only one instance of
* {@code AuthzeroTokenResolverConfig} is created and shared throughout the
* application. The singleton pattern is implemented to provide centralised
* configuration and avoid redundant object creation.
*/
private static AuthzeroTokenResolverConfig instance;
/**
* The supported algorithms and their corresponding algorithm functions.
* <p>
* This map stores the supported algorithms as keys and their corresponding
* algorithm functions as values. The algorithm functions represent the
* functions used by the {@code com.auth0:java-jwt} library to handle the
* specific algorithms. The mapping is used to provide proper algorithm
* resolution and processing within the {@link AuthzeroTokenResolver}.
*/
private static final Map<TokenAlgorithm, Function<String, Algorithm>> SUPPORTED_ALGORITHMS = new HashMap<>() {{
put(TokenAlgorithm.HS256, Algorithm::HMAC256);
put(TokenAlgorithm.HS384, Algorithm::HMAC384);
put(TokenAlgorithm.HS512, Algorithm::HMAC512);
}};
} }
@@ -18,37 +18,37 @@
/** /**
* The package {@code cn.org.codecrafters.simplejwt.authzero.config} contains * The package {@code cn.org.codecrafters.simplejwt.authzero.config} contains
* configuration classes related to the {@link * configuration classes related to the {@link
* cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver} * com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver}
* implementation. * implementation.
* <p> * <p>
* The classes in this package provide configuration options and settings for * The classes in this package provide configuration options and settings for
* the {@link cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver}, * the {@link com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver},
* which is used for resolving JSON Web Tokens (JWT) using the Auth0 library. * which is used for resolving JSON Web Tokens (JWT) using the Auth0 library.
* <p> * <p>
* The {@link * The {@link
* cn.org.codecrafters.simplejwt.authzero.config.AuthzeroTokenResolverConfig} * com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig}
* class is a configuration class that defines the mapping between standard * class is a configuration class that defines the mapping between standard
* {@link cn.org.codecrafters.simplejwt.constants.TokenAlgorithm} and the * {@link com.onixbyte.simplejwt.constants.TokenAlgorithm} and the
* corresponding function implementation used by {@link * corresponding function implementation used by {@link
* cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver} for handling * com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver} for handling
* JWT algorithms. It enables developers to specify and customize the * JWT algorithms. It enables developers to specify and customize the
* algorithm functions according to the chosen JWT algorithm and the library * algorithm functions according to the chosen JWT algorithm and the library
* being used. * being used.
* <p> * <p>
* The configuration options in this package help developers integrate and * The configuration options in this package help developers integrate and
* configure the {@link * configure the {@link
* cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver} seamlessly * com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver} seamlessly
* into their Spring Boot applications. Developers can fine-tune the token * into their Spring Boot applications. Developers can fine-tune the token
* resolution process and customize algorithm handling to align with their * resolution process and customize algorithm handling to align with their
* specific requirements and desired level of security. * specific requirements and desired level of security.
* <p> * <p>
* It is recommended to explore the classes in this package to understand how * It is recommended to explore the classes in this package to understand how
* to configure and use the {@link * to configure and use the {@link
* cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver} effectively * com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver} effectively
* in the Spring Boot environment to handle JWT authentication and * in the Spring Boot environment to handle JWT authentication and
* authorisation securely and efficiently. * authorisation securely and efficiently.
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.authzero.config; package com.onixbyte.simplejwt.authzero.config;
@@ -25,15 +25,15 @@
* the {@code com.auth0:java-jwt} library. * the {@code com.auth0:java-jwt} library.
* <p> * <p>
* The main class in this package is the {@link * The main class in this package is the {@link
* cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver}, which * com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver}, which
* implements the {@link cn.org.codecrafters.simplejwt.TokenResolver} interface * implements the {@link com.onixbyte.simplejwt.TokenResolver} interface
* and uses the {@code com.auth0:java-jwt} library to handle JWT operations. It * and uses the {@code com.auth0:java-jwt} library to handle JWT operations. It
* provides the functionality to create, validate, and extract JWTs using the * provides the functionality to create, validate, and extract JWTs using the
* {@code com.auth0:java-jwt} library. Developers can use this class as the * {@code com.auth0:java-jwt} library. Developers can use this class as the
* main token resolver in the Simple JWT project when integrating {@code * main token resolver in the Simple JWT project when integrating {@code
* com.auth0:java-jwt} as the JWT management library. * com.auth0:java-jwt} as the JWT management library.
* <p> * <p>
* The {@link cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver} * The {@link com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver}
* relies on the {@code com.auth0:java-jwt} library to handle the underlying * relies on the {@code com.auth0:java-jwt} library to handle the underlying
* JWT operations, including token creation, validation, and extraction. It * JWT operations, including token creation, validation, and extraction. It
* utilizes the {@code com.auth0:java-jwt} {@link * utilizes the {@code com.auth0:java-jwt} {@link
@@ -41,12 +41,12 @@
* algorithms for JWT signing and verification. * algorithms for JWT signing and verification.
* <p> * <p>
* To use the {@link * To use the {@link
* cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver}, developers * com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver}, developers
* must provide the necessary configurations and dependencies, such as the * must provide the necessary configurations and dependencies, such as the
* {@link cn.org.codecrafters.guid.GuidCreator} for generating unique JWT IDs * {@link com.onixbyte.guid.GuidCreator} for generating unique JWT IDs
* (JTI), the supported algorithm function, the issuer name, and the secret key * (JTI), the supported algorithm function, the issuer name, and the secret key
* used for token signing and validation. The {@link * used for token signing and validation. The {@link
* cn.org.codecrafters.simplejwt.authzero.config.AuthzeroTokenResolverConfig} * com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig}
* class provides a convenient way to configure these dependencies. * class provides a convenient way to configure these dependencies.
* <p> * <p>
* Developers using the {@code com.auth0:java-jwt} integration should be * Developers using the {@code com.auth0:java-jwt} integration should be
@@ -56,4 +56,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.authzero; package com.onixbyte.simplejwt.authzero;
@@ -15,10 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.authzero.test; package com.onixbyte.simplejwt.authzero.test;
import cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver; import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.time.Duration; import java.time.Duration;
@@ -15,9 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt; package com.onixbyte.simplejwt;
import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException; import com.onixbyte.simplejwt.exceptions.WeakSecretException;
import java.util.Random; import java.util.Random;
@@ -32,36 +32,6 @@ import java.util.Random;
*/ */
public final class SecretCreator { public final class SecretCreator {
/**
* Private constructor to prevent instantiation
*/
private SecretCreator() {
}
/**
* The string containing all lowercase characters that can be used to
* generate the secret.
*/
private static final String LOWERCASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz";
/**
* The string containing all uppercase characters that can be used to
* generate the secret.
*/
private static final String UPPERCASE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* The string containing all digit characters that can be used to generate
* the secret.
*/
private static final String DIGITS = "0123456789";
/**
* The string containing all special sign characters that can be used to
* generate the secret.
*/
private static final String SPECIAL_SIGNS = "!@#$%^&,*()_+-=,[]{}|;:,'\",.<>/?";
/** /**
* Generates a secure secret with the specified length and character sets. * Generates a secure secret with the specified length and character sets.
* *
@@ -153,4 +123,33 @@ public final class SecretCreator {
return createSecret(length, false, false, false); return createSecret(length, false, false, false);
} }
/**
* Private constructor will protect this class from being instantiated.
*/
private SecretCreator() {
}
/**
* The string containing all lowercase characters that can be used to
* generate the secret.
*/
private static final String LOWERCASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz";
/**
* The string containing all uppercase characters that can be used to
* generate the secret.
*/
private static final String UPPERCASE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* The string containing all digit characters that can be used to generate
* the secret.
*/
private static final String DIGITS = "0123456789";
/**
* The string containing all special sign characters that can be used to
* generate the secret.
*/
private static final String SPECIAL_SIGNS = "!@#$%^&,*()_+-=,[]{}|;:,'\",.<>/?";
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt; package com.onixbyte.simplejwt;
import java.util.Map; import java.util.Map;
@@ -15,9 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt; package com.onixbyte.simplejwt;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration; import java.time.Duration;
import java.util.Map; import java.util.Map;
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.annotations; package com.onixbyte.simplejwt.annotations;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -15,9 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.annotations; package com.onixbyte.simplejwt.annotations;
import cn.org.codecrafters.simplejwt.constants.TokenDataType; import com.onixbyte.simplejwt.constants.TokenDataType;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -24,4 +24,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.annotations; package com.onixbyte.simplejwt.annotations;
@@ -15,10 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.config; package com.onixbyte.simplejwt.config;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
/** /**
* The {@code TokenResolverConfig} provides a mechanism to configure an * The {@code TokenResolverConfig} provides a mechanism to configure an
@@ -29,4 +29,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.config; package com.onixbyte.simplejwt.config;
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.constants; package com.onixbyte.simplejwt.constants;
import java.util.List; import java.util.List;
@@ -88,11 +88,7 @@ public final class PredefinedKeys {
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 to prevent instantiation of the * Private constructor will protect this class from being instantiated.
* {@code PredefinedKeys} class.
* <p>
* This class is intended to be used as a utility class with only static
* constants and methods.
*/ */
private PredefinedKeys() { private PredefinedKeys() {
} }
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.constants; package com.onixbyte.simplejwt.constants;
import lombok.Getter; import lombok.Getter;
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.constants; package com.onixbyte.simplejwt.constants;
import lombok.Getter; import lombok.Getter;
@@ -63,6 +63,9 @@ public enum TokenDataType {
*/ */
private final Class<?> mappedClass; private final Class<?> mappedClass;
/**
* Create a TokenDataType with a mapped class.
*/
TokenDataType(Class<?> mappedClass) { TokenDataType(Class<?> mappedClass) {
this.mappedClass = mappedClass; this.mappedClass = mappedClass;
} }
@@ -23,4 +23,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.constants; package com.onixbyte.simplejwt.constants;
@@ -15,12 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.exceptions; package com.onixbyte.simplejwt.exceptions;
import com.onixbyte.simplejwt.TokenResolver;
/** /**
* This {@code UnsupportedAlgorithmException} represents the given * This {@code UnsupportedAlgorithmException} represents the given
* algorithm is not supported by {@link * algorithm is not supported by {@link
* cn.org.codecrafters.simplejwt.TokenResolver} yet. * TokenResolver} yet.
* <p> * <p>
* If you want the supports to an unsupported algorithm, you could * If you want the supports to an unsupported algorithm, you could
* <ul> * <ul>
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.exceptions; package com.onixbyte.simplejwt.exceptions;
/** /**
* {@code WeakSecretException} represents that your secret is too weak to be * {@code WeakSecretException} represents that your secret is too weak to be
@@ -36,4 +36,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.exceptions; package com.onixbyte.simplejwt.exceptions;
@@ -37,4 +37,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt; package com.onixbyte.simplejwt;
+1 -1
View File
@@ -45,7 +45,7 @@ implementation 'cn.org.codecrafters:simple-jwt-authzero:${simple-jwt-authzero.ve
## Use the `JjwtTokenResolver` ## Use the `JjwtTokenResolver`
We have implemented `TokenResolver` to make sure you can add JWT to your Java application as soon as possible. All you need to do is to create an instance of `cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver` and other operations to JWT could follow our instruction in [`simple-jwt-facade`](../simple-jwt-facade/README.md). We have implemented `TokenResolver` to make sure you can add JWT to your Java application as soon as possible. All you need to do is to create an instance of `jjwt.com.onixbyte.simplejwt.JjwtTokenResolver` and other operations to JWT could follow our instruction in [`simple-jwt-facade`](../simple-jwt-facade/README.md).
## Contact ## Contact
@@ -15,19 +15,19 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.jjwt; package com.onixbyte.simplejwt.jjwt;
import cn.org.codecrafters.devkit.utils.MapUtil; import com.onixbyte.devkit.utils.MapUtil;
import cn.org.codecrafters.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import cn.org.codecrafters.simplejwt.SecretCreator; import com.onixbyte.simplejwt.SecretCreator;
import cn.org.codecrafters.simplejwt.TokenPayload; import com.onixbyte.simplejwt.TokenPayload;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.annotations.ExcludeFromPayload; import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
import cn.org.codecrafters.simplejwt.annotations.TokenEnum; import com.onixbyte.simplejwt.annotations.TokenEnum;
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys; import com.onixbyte.simplejwt.constants.PredefinedKeys;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException; import com.onixbyte.simplejwt.exceptions.WeakSecretException;
import cn.org.codecrafters.simplejwt.jjwt.config.JjwtTokenResolverConfig; import com.onixbyte.simplejwt.jjwt.config.JjwtTokenResolverConfig;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
@@ -98,16 +98,6 @@ import java.util.*;
@Slf4j @Slf4j
public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> { public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
private final GuidCreator<?> jtiCreator;
private final SecureDigestAlgorithm<SecretKey, SecretKey> algorithm;
private final String issuer;
private final SecretKey key;
private final JjwtTokenResolverConfig config = JjwtTokenResolverConfig.getInstance();
/** /**
* Create a resolver with specified algorithm, issuer, secret and guid strategy. * Create a resolver with specified algorithm, issuer, secret and guid strategy.
* *
@@ -432,6 +422,15 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
return renew(oldToken, Duration.ofMinutes(30), payload); return renew(oldToken, Duration.ofMinutes(30), payload);
} }
/**
* Build a new token with specified data.
*
* @param expireAfter the validity time of the token
* @param audience the audience of the token
* @param subject the subject of the token
* @param claims the data to be included in the token
* @return the built token
*/
private String buildToken(Duration expireAfter, String audience, String subject, Map<String, Object> claims) { private String buildToken(Duration expireAfter, String audience, String subject, Map<String, Object> claims) {
var now = LocalDateTime.now(); var now = LocalDateTime.now();
var builder = Jwts.builder() var builder = Jwts.builder()
@@ -453,4 +452,29 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
return builder.signWith(key, algorithm) return builder.signWith(key, algorithm)
.compact(); .compact();
} }
/**
* The ID creator for creating unique JWT IDs.
*/
private final GuidCreator<?> jtiCreator;
/**
* The algorithm to sign this token.
*/
private final SecureDigestAlgorithm<SecretKey, SecretKey> algorithm;
/**
* The issuer of this token.
*/
private final String issuer;
/**
* The signature key of this token.
*/
private final SecretKey key;
/**
* The config of this token resolver.
*/
private final JjwtTokenResolverConfig config = JjwtTokenResolverConfig.getInstance();
} }
@@ -15,16 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.jjwt.config; package com.onixbyte.simplejwt.jjwt.config;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.config.TokenResolverConfig; import com.onixbyte.simplejwt.config.TokenResolverConfig;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
import cn.org.codecrafters.simplejwt.exceptions.UnsupportedAlgorithmException; import com.onixbyte.simplejwt.exceptions.UnsupportedAlgorithmException;
import cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver; import com.onixbyte.simplejwt.jjwt.JjwtTokenResolver;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.MacAlgorithm;
import io.jsonwebtoken.security.SecureDigestAlgorithm; import io.jsonwebtoken.security.SecureDigestAlgorithm;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
@@ -63,17 +61,6 @@ import java.util.Map;
*/ */
public final class JjwtTokenResolverConfig implements TokenResolverConfig<SecureDigestAlgorithm<SecretKey, SecretKey>> { public final class JjwtTokenResolverConfig implements TokenResolverConfig<SecureDigestAlgorithm<SecretKey, SecretKey>> {
private JjwtTokenResolverConfig() {
}
private static final Map<TokenAlgorithm, SecureDigestAlgorithm<SecretKey, SecretKey>> SUPPORTED_ALGORITHMS = new HashMap<>() {{
put(TokenAlgorithm.HS256, Jwts.SIG.HS256);
put(TokenAlgorithm.HS384, Jwts.SIG.HS384);
put(TokenAlgorithm.HS512, Jwts.SIG.HS512);
}};
private static JjwtTokenResolverConfig instance;
public static JjwtTokenResolverConfig getInstance() { public static JjwtTokenResolverConfig getInstance() {
if (instance == null) { if (instance == null) {
instance = new JjwtTokenResolverConfig(); instance = new JjwtTokenResolverConfig();
@@ -106,4 +93,25 @@ public final class JjwtTokenResolverConfig implements TokenResolverConfig<Secure
} }
return SUPPORTED_ALGORITHMS.get(algorithm); return SUPPORTED_ALGORITHMS.get(algorithm);
} }
/**
* Private constructor will protect this class from being instantiated.
*/
private JjwtTokenResolverConfig() {
}
/**
* A {@code Map} to map a {@link TokenAlgorithm} to {@link SecureDigestAlgorithm}.
*/
private static final Map<TokenAlgorithm, SecureDigestAlgorithm<SecretKey, SecretKey>> SUPPORTED_ALGORITHMS = new HashMap<>() {{
put(TokenAlgorithm.HS256, Jwts.SIG.HS256);
put(TokenAlgorithm.HS384, Jwts.SIG.HS384);
put(TokenAlgorithm.HS512, Jwts.SIG.HS512);
}};
/**
* The instance of this config class.
*/
private static JjwtTokenResolverConfig instance;
} }
@@ -18,36 +18,36 @@
/** /**
* The package {@code cn.org.codecrafters.simplejwt.jjwt.config} contains * The package {@code cn.org.codecrafters.simplejwt.jjwt.config} contains
* configuration classes related to the {@link * configuration classes related to the {@link
* cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver} * com.onixbyte.simplejwt.jjwt.JjwtTokenResolver}
* implementation. * implementation.
* <p> * <p>
* The classes in this package provide configuration options and settings for * The classes in this package provide configuration options and settings for
* the {@link cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver}, * the {@link com.onixbyte.simplejwt.jjwt.JjwtTokenResolver},
* which is used for resolving JSON Web Tokens (JWT) using the Auth0 library. * which is used for resolving JSON Web Tokens (JWT) using the Auth0 library.
* <p> * <p>
* The {@link * The {@link
* cn.org.codecrafters.simplejwt.jjwt.config.JjwtTokenResolverConfig} * com.onixbyte.simplejwt.jjwt.config.JjwtTokenResolverConfig}
* class is a configuration class that defines the mapping between standard * class is a configuration class that defines the mapping between standard
* {@link cn.org.codecrafters.simplejwt.constants.TokenAlgorithm} and the * {@link com.onixbyte.simplejwt.constants.TokenAlgorithm} and the
* corresponding function implementation used by {@link * corresponding function implementation used by {@link
* cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver} for handling * com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} for handling
* JWT algorithms. It enables developers to specify and customize the * JWT algorithms. It enables developers to specify and customize the
* algorithm functions according to the chosen JWT algorithm and the library * algorithm functions according to the chosen JWT algorithm and the library
* being used. * being used.
* <p> * <p>
* The configuration options in this package help developers integrate and * The configuration options in this package help developers integrate and
* configure the {@link * configure the {@link
* cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver} seamlessly * com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} seamlessly
* into their Spring Boot applications. Developers can fine-tune the token * into their Spring Boot applications. Developers can fine-tune the token
* resolution process and customize algorithm handling to align with their * resolution process and customize algorithm handling to align with their
* specific requirements and desired level of security. * specific requirements and desired level of security.
* <p> * <p>
* It is recommended to explore the classes in this package to understand how * It is recommended to explore the classes in this package to understand how
* to configure and use the {@link * to configure and use the {@link
* cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver} effectively * com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} effectively
* in the Spring Boot environment to handle JWT authentication and * in the Spring Boot environment to handle JWT authentication and
* authorisation securely and efficiently. * authorisation securely and efficiently.
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.jjwt.config; package com.onixbyte.simplejwt.jjwt.config;
@@ -25,27 +25,27 @@
* the {@code io.jsonwebtoken:jjwt-api} library. * the {@code io.jsonwebtoken:jjwt-api} library.
* <p> * <p>
* The main class in this package is the {@link * The main class in this package is the {@link
* cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver}, which * com.onixbyte.simplejwt.jjwt.JjwtTokenResolver}, which
* implements the {@link cn.org.codecrafters.simplejwt.TokenResolver} interface * implements the {@link com.onixbyte.simplejwt.TokenResolver} interface
* and uses the {@code io.jsonwebtoken:jjwt-api} library to handle JWT * and uses the {@code io.jsonwebtoken:jjwt-api} library to handle JWT
* operations. It provides the functionality to create, validate, and extract * operations. It provides the functionality to create, validate, and extract
* JWTs using the {@code io.jsonwebtoken:jjwt-api} library. Developers can use * JWTs using the {@code io.jsonwebtoken:jjwt-api} library. Developers can use
* this class as the main token resolver in the Simple JWT project when * this class as the main token resolver in the Simple JWT project when
* integrating {@code io.jsonwebtoken:jjwt-api} as the JWT management library. * integrating {@code io.jsonwebtoken:jjwt-api} as the JWT management library.
* <p> * <p>
* The {@link cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver} relies on * The {@link com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} relies on
* the {@code io.jsonwebtoken:jjwt-api} * the {@code io.jsonwebtoken:jjwt-api}
* library to handle the underlying JWT operations, including token creation, * library to handle the underlying JWT operations, including token creation,
* validation, and extraction. It utilizes the {@code io.jsonwebtoken:jjwt-api} * validation, and extraction. It utilizes the {@code io.jsonwebtoken:jjwt-api}
* {@link io.jsonwebtoken.SignatureAlgorithm} class to define and use different * {@link io.jsonwebtoken.SignatureAlgorithm} class to define and use different
* algorithms for JWT signing and verification. * algorithms for JWT signing and verification.
* <p> * <p>
* To use the {@link cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver}, * To use the {@link com.onixbyte.simplejwt.jjwt.JjwtTokenResolver},
* developers must provide the necessary configurations and dependencies, such * developers must provide the necessary configurations and dependencies, such
* as the {@link cn.org.codecrafters.guid.GuidCreator} for generating unique * as the {@link com.onixbyte.guid.GuidCreator} for generating unique
* JWT IDs (JTI), the supported algorithm function, the issuer name, and the * JWT IDs (JTI), the supported algorithm function, the issuer name, and the
* secret key used for token signing and validation. The * secret key used for token signing and validation. The
* {@link cn.org.codecrafters.simplejwt.jjwt.config.JjwtTokenResolverConfig} * {@link com.onixbyte.simplejwt.jjwt.config.JjwtTokenResolverConfig}
* class provides a convenient way to configure these dependencies. * class provides a convenient way to configure these dependencies.
* <p> * <p>
* Developers using the {@code io.jsonwebtoken:jjwt-api} integration should be * Developers using the {@code io.jsonwebtoken:jjwt-api} integration should be
@@ -55,4 +55,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.jjwt; package com.onixbyte.simplejwt.jjwt;
+1 -1
View File
@@ -66,7 +66,7 @@ implementation 'cn.org.codecrafters:simple-jwt-spring-boot-starter:${simple-jwt-
We need a `GuidCreator` instance to create JWT ID, though we did implemented a simple `GuidCreator`, but you can still customize it. We need a `GuidCreator` instance to create JWT ID, though we did implemented a simple `GuidCreator`, but you can still customize it.
First, please implement the `cn.org.codecrafters.guid.GuidCreator` interface based on your own rules for generating JWT IDs. First, please implement the `com.onixbyte.guid.GuidCreator` interface based on your own rules for generating JWT IDs.
Then, add the instance of your own guid creator to spring container, whose name is `jtiCreator`. Then, add the instance of your own guid creator to spring container, whose name is `jtiCreator`.
@@ -15,12 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.autoconfiguration; package com.onixbyte.simplejwt.autoconfiguration;
import cn.org.codecrafters.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver; import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
import cn.org.codecrafters.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 lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -65,19 +65,6 @@ import org.springframework.context.annotation.Bean;
@AutoConfigureAfter(value = GuidAutoConfiguration.class) @AutoConfigureAfter(value = GuidAutoConfiguration.class)
public class AuthzeroTokenResolverAutoConfiguration { public class AuthzeroTokenResolverAutoConfiguration {
/**
* The GuidCreator instance to be used for generating JWT IDs (JTI).
*/
private final GuidCreator<?> jtiCreator;
/**
* The {@code SimpleJwtProperties} instance containing the configuration
* properties for Simple JWT.
*/
private final SimpleJwtProperties simpleJwtProperties;
private final ObjectMapper objectMapper;
/** /**
* Constructs a new {@code SimpleJwtAutoConfiguration} instance with the * Constructs a new {@code SimpleJwtAutoConfiguration} instance with the
* provided SimpleJwtProperties. * provided SimpleJwtProperties.
@@ -112,4 +99,17 @@ public class AuthzeroTokenResolverAutoConfiguration {
); );
} }
/**
* The GuidCreator instance to be used for generating JWT IDs (JTI).
*/
private final GuidCreator<?> jtiCreator;
/**
* The {@code SimpleJwtProperties} instance containing the configuration
* properties for Simple JWT.
*/
private final SimpleJwtProperties simpleJwtProperties;
private final ObjectMapper objectMapper;
} }
@@ -15,14 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.autoconfiguration; package com.onixbyte.simplejwt.autoconfiguration;
import cn.org.codecrafters.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import cn.org.codecrafters.simplejwt.autoconfiguration.conditions.GuidCreatorCondition; import com.onixbyte.simplejwt.autoconfiguration.conditions.GuidCreatorCondition;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
@@ -39,6 +37,11 @@ import java.util.UUID;
@AutoConfiguration @AutoConfiguration
public class GuidAutoConfiguration { public class GuidAutoConfiguration {
/**
* Create a default {@code jtiCreator} with UUID.
*
* @return UUID creator
*/
@Bean(name = "jtiCreator") @Bean(name = "jtiCreator")
@Conditional(GuidCreatorCondition.class) @Conditional(GuidCreatorCondition.class)
public GuidCreator<?> jtiCreator() { public GuidCreator<?> jtiCreator() {
@@ -15,12 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.autoconfiguration; package com.onixbyte.simplejwt.autoconfiguration;
import cn.org.codecrafters.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import cn.org.codecrafters.simplejwt.TokenResolver; import com.onixbyte.simplejwt.TokenResolver;
import cn.org.codecrafters.simplejwt.autoconfiguration.properties.SimpleJwtProperties; import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
import cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver; import com.onixbyte.simplejwt.jjwt.JjwtTokenResolver;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jws;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -65,17 +65,6 @@ import org.springframework.context.annotation.Bean;
@AutoConfigureAfter(value = GuidAutoConfiguration.class) @AutoConfigureAfter(value = GuidAutoConfiguration.class)
public class JjwtTokenResolverAutoConfiguration { public class JjwtTokenResolverAutoConfiguration {
/**
* The GuidCreator instance to be used for generating JWT IDs (JTI).
*/
private final GuidCreator<?> jtiCreator;
/**
* The {@code SimpleJwtProperties} instance containing the configuration
* properties for Simple JWT.
*/
private final SimpleJwtProperties simpleJwtProperties;
/** /**
* Constructs a new {@code SimpleJwtAutoConfiguration} instance with the * Constructs a new {@code SimpleJwtAutoConfiguration} instance with the
* provided SimpleJwtProperties. * provided SimpleJwtProperties.
@@ -107,4 +96,15 @@ public class JjwtTokenResolverAutoConfiguration {
); );
} }
/**
* The GuidCreator instance to be used for generating JWT IDs (JTI).
*/
private final GuidCreator<?> jtiCreator;
/**
* The {@code SimpleJwtProperties} instance containing the configuration
* properties for Simple JWT.
*/
private final SimpleJwtProperties simpleJwtProperties;
} }
@@ -1,6 +1,6 @@
package cn.org.codecrafters.simplejwt.autoconfiguration.conditions; package com.onixbyte.simplejwt.autoconfiguration.conditions;
import cn.org.codecrafters.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
@@ -17,6 +17,18 @@ import java.util.Objects;
*/ */
@Slf4j @Slf4j
public class GuidCreatorCondition implements Condition { public class GuidCreatorCondition implements Condition {
/**
* The condition to create bean {@code jtiCreator}.
* <p>
* If Spring does not have a bean of type
* {@link GuidCreator} named {@code jtiCreator}
* in the application context, then create {@code jtiCreator}.
*
* @param context the spring application context
* @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
*/
@Override @Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
final var beanFactory = Objects.requireNonNull(context.getBeanFactory()); final var beanFactory = Objects.requireNonNull(context.getBeanFactory());
@@ -35,4 +35,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.autoconfiguration; package com.onixbyte.simplejwt.autoconfiguration;
@@ -15,11 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.simplejwt.autoconfiguration.properties; package com.onixbyte.simplejwt.autoconfiguration.properties;
import cn.org.codecrafters.simplejwt.SecretCreator; import com.onixbyte.simplejwt.SecretCreator;
import cn.org.codecrafters.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration; import com.onixbyte.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.constants.TokenAlgorithm;
import com.onixbyte.simplejwt.jjwt.JjwtTokenResolver;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -32,7 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* {@code SimpleJwtProperties} provides configuration options for the JWT * {@code SimpleJwtProperties} provides configuration options for the JWT
* algorithm, issuer, and secret. The properties are used by the {@link * algorithm, issuer, and secret. The properties are used by the {@link
* AuthzeroTokenResolverAutoConfiguration} and {@link * AuthzeroTokenResolverAutoConfiguration} and {@link
* cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver} to set up the * JjwtTokenResolver} to set up the
* necessary configurations for JWT generation and validation. * necessary configurations for JWT generation and validation.
* <p> * <p>
* Developers can customise the JWT algorithm, issuer, and secret by setting * Developers can customise the JWT algorithm, issuer, and secret by setting
@@ -45,7 +46,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @since 1.0.0 * @since 1.0.0
*/ */
@Data @Data
@ConfigurationProperties(prefix = "code-crafters.simple-jwt") @ConfigurationProperties(prefix = "onixbyte.simple-jwt")
public class SimpleJwtProperties { public class SimpleJwtProperties {
/** /**
@@ -27,9 +27,9 @@
* the corresponding properties in the application's properties file with the * the corresponding properties in the application's properties file with the
* prefix "code-crafters.simple-jwt". The SimpleJwtAutoConfiguration class * prefix "code-crafters.simple-jwt". The SimpleJwtAutoConfiguration class
* reads these properties and uses them to create the {@link * reads these properties and uses them to create the {@link
* cn.org.codecrafters.simplejwt.TokenResolver} bean with the desired * com.onixbyte.simplejwt.TokenResolver} bean with the desired
* configuration. * configuration.
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.simplejwt.autoconfiguration.properties; package com.onixbyte.simplejwt.autoconfiguration.properties;
@@ -1,3 +1,3 @@
cn.org.codecrafters.simplejwt.autoconfiguration.GuidAutoConfiguration com.onixbyte.simplejwt.autoconfiguration.GuidAutoConfiguration
cn.org.codecrafters.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration com.onixbyte.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration
cn.org.codecrafters.simplejwt.autoconfiguration.JjwtTokenResolverAutoConfiguration com.onixbyte.simplejwt.autoconfiguration.JjwtTokenResolverAutoConfiguration
@@ -15,7 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.webcal; package com.onixbyte.webcal;
import com.onixbyte.webcal.impl.WebCalendarEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -50,55 +52,6 @@ import java.util.Objects;
*/ */
public final class WebCalendar { public final class WebCalendar {
/**
* The {@code VCALENDAR} tag for iCalendar format
*/
private final static String TAG = "VCALENDAR";
/**
* The name of this calendar.
*/
private String name;
/**
* The company who produces this calendar.
* <p>
* This property will be used in {@code PRODID}.
*/
private String companyName;
/**
* The product name.
* <p>
* This property will be used in {@code PRODID}
*/
private String productName;
/**
* The producer's domain name.
*/
private String domainName;
/**
* Scale of this calendar.
*/
private final String scale = "GREGORIAN";
/**
* The method of this calendar.
*/
private String method;
/**
* The version of this calendar.
*/
private final String version = "2.0";
/**
* List of calendar components and events
*/
private final List<WebCalendarNode> nodes;
/** /**
* Constructor for WebCalendar class, initializes the list of calendar * Constructor for WebCalendar class, initializes the list of calendar
* components and events. * components and events.
@@ -209,5 +162,54 @@ public final class WebCalendar {
"END:" + TAG; "END:" + TAG;
} }
/**
* The {@code VCALENDAR} tag for iCalendar format
*/
private final static String TAG = "VCALENDAR";
/**
* The name of this calendar.
*/
private String name;
/**
* The company who produces this calendar.
* <p>
* This property will be used in {@code PRODID}.
*/
private String companyName;
/**
* The product name.
* <p>
* This property will be used in {@code PRODID}
*/
private String productName;
/**
* The producer's domain name.
*/
private String domainName;
/**
* Scale of this calendar.
*/
private final String scale = "GREGORIAN";
/**
* The method of this calendar.
*/
private String method;
/**
* The version of this calendar.
*/
private final String version = "2.0";
/**
* List of calendar components and events
*/
private final List<WebCalendarNode> nodes;
} }
@@ -15,9 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.webcal; package com.onixbyte.webcal;
import cn.org.codecrafters.webcal.config.Classification; import com.onixbyte.webcal.config.Classification;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -37,8 +37,7 @@ import java.util.List;
* @version 1.1.0 * @version 1.1.0
* @since 1.0.0 * @since 1.0.0
*/ */
public abstract sealed class WebCalendarNode public abstract class WebCalendarNode {
permits WebCalendarEvent {
// Common properties for all calendar components and events // Common properties for all calendar components and events
protected List<String> categories; protected List<String> categories;
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.webcal.config; package com.onixbyte.webcal.config;
import lombok.Getter; import lombok.Getter;
@@ -15,20 +15,26 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.webcal.config; package com.onixbyte.webcal.config;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Objects; import java.util.Objects;
/** /**
* DatetimeFormatters * A formatter to format {@link java.time.LocalDateTime}.
* *
* @author Zihlu Wang * @author Zihlu Wang
* @since 21 Sept, 2023
*/ */
public final class Formatter { public final class DateAndTimeFormatter {
/**
* Get the {@link java.time.format.DateTimeFormatter datetime formatter}
* with UTC pattern and timezone.
*
* @return the {@link java.time.format.DateTimeFormatter datetime formatter}
* with UTC pattern and timezone
*/
public static DateTimeFormatter getUtcDatetimeFormatter() { public static DateTimeFormatter getUtcDatetimeFormatter() {
if (Objects.isNull(utcDateTimeFormatter)) { if (Objects.isNull(utcDateTimeFormatter)) {
utcDateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").withZone(ZoneOffset.UTC); utcDateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").withZone(ZoneOffset.UTC);
@@ -37,16 +43,6 @@ public final class Formatter {
return utcDateTimeFormatter; return utcDateTimeFormatter;
} }
// public static DateTimeFormatter getLocalDatetimeFormatter() {
// if (Objects.isNull(localDatetimeFormatter)) {
// localDatetimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss");
// }
//
// return localDatetimeFormatter;
// }
private static DateTimeFormatter utcDateTimeFormatter; private static DateTimeFormatter utcDateTimeFormatter;
// private static DateTimeFormatter localDatetimeFormatter;
} }
@@ -23,11 +23,11 @@
* <p>The classes in this package include:</p> * <p>The classes in this package include:</p>
* <ul> * <ul>
* <li> * <li>
* {@link cn.org.codecrafters.webcal.config.Classification}: An enum * {@link com.onixbyte.webcal.config.Classification}: An enum
* representing the classification of events in the web calendar. * representing the classification of events in the web calendar.
* </li> * </li>
* </ul> * </ul>
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.webcal.config; package com.onixbyte.webcal.config;
@@ -15,10 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.webcal; package com.onixbyte.webcal.impl;
import cn.org.codecrafters.webcal.config.Classification; import com.onixbyte.webcal.WebCalendarNode;
import cn.org.codecrafters.webcal.config.Formatter; import com.onixbyte.webcal.config.Classification;
import com.onixbyte.webcal.config.DateAndTimeFormatter;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.Duration; import java.time.Duration;
@@ -47,8 +48,6 @@ import java.util.UUID;
*/ */
public final class WebCalendarEvent extends WebCalendarNode { public final class WebCalendarEvent extends WebCalendarNode {
private final static String TAG = "VEVENT";
/** /**
* Add categories to the event. * Add categories to the event.
* *
@@ -268,25 +267,27 @@ public final class WebCalendarEvent extends WebCalendarNode {
DURATION:PT{6}S DURATION:PT{6}S
{4}{5}{7}{8}{9}{10}{11}{12} {4}{5}{7}{8}{9}{10}{11}{12}
END:{0}""", END:{0}""",
TAG, // 0 - tag /* 0 - tag */TAG,
Optional.ofNullable(uid).orElse(UUID.randomUUID().toString()) + "@" + domainName, // 1 - uid /* 1 - uid */ Optional.ofNullable(uid).orElse(UUID.randomUUID().toString()) + "@" + domainName,
now.format(Formatter.getUtcDatetimeFormatter()), // 2 - dtstamp /* 2 - dtstamp */ now.format(DateAndTimeFormatter.getUtcDatetimeFormatter()),
start.atZone(ZoneId.systemDefault()).format(Formatter.getUtcDatetimeFormatter()), // 3 - start time /* 3 - start time */ start.atZone(ZoneId.systemDefault()).format(DateAndTimeFormatter.getUtcDatetimeFormatter()),
Optional.ofNullable(summary).map((item) -> "\nSUMMARY:" + item).orElse(""), // 4 - summary /* 4 - summary */ Optional.ofNullable(summary).map((item) -> "\nSUMMARY:" + item).orElse(""),
Optional.ofNullable(categories) /* 5 - categories */ Optional.ofNullable(categories)
.map((item) -> !item.isEmpty() ? "\nCATEGORIES:" + resolveCategories() : null).orElse(""), // 5 - categories .map((item) -> !item.isEmpty() ? "\nCATEGORIES:" + resolveCategories() : null).orElse(""),
Optional.ofNullable(duration) /* 6 - duration */ Optional.ofNullable(duration)
.map((_duration) -> String.valueOf(_duration.getSeconds())) .map((_duration) -> String.valueOf(_duration.getSeconds()))
.orElse(Optional.ofNullable(end) .orElse(Optional.ofNullable(end)
.map((_end) -> String.valueOf(Duration.between(_end, start).getSeconds())) .map((_end) -> String.valueOf(Duration.between(_end, start).getSeconds()))
.orElse("0")), // 6 - duration .orElse("0")),
Optional.ofNullable(classification).map((_classification) -> "\nCLASS:" + _classification + "\n").orElse(""), /* 7 - classification */ /* 7 - classification */ Optional.ofNullable(classification).map((_classification) -> "\nCLASS:" + _classification + "\n").orElse(""),
Optional.ofNullable(comment).map((_comment) -> "\nCOMMENT:" + _comment + "\n").orElse(""), /* 8 - comment */ /* 8 - comment */ Optional.ofNullable(comment).map((_comment) -> "\nCOMMENT:" + _comment + "\n").orElse(""),
Optional.ofNullable(location).map((_location) -> "\nLOCATION:" + _location).orElse("") /* 9 - location */, /* 9 - location */ Optional.ofNullable(location).map((_location) -> "\nLOCATION:" + _location).orElse(""),
Optional.ofNullable(percentComplete).map((_percentComplete) -> "\nPERCENT-COMPLETE:" + _percentComplete).orElse("") /* 10 = percentComplete */, /* 10 = percentComplete */ Optional.ofNullable(percentComplete).map((_percentComplete) -> "\nPERCENT-COMPLETE:" + _percentComplete).orElse(""),
Optional.ofNullable(description).map((_description) -> "\nDESCRIPTION:" + _description).orElse("") /* 11 - description */, /* 11 - description */ Optional.ofNullable(description).map((_description) -> "\nDESCRIPTION:" + _description).orElse(""),
Optional.ofNullable(priority).map((_priority) -> "\nPRIORITY:" + _priority).orElse("") /* 12 - priority */ /* 12 - priority */ Optional.ofNullable(priority).map((_priority) -> "\nPRIORITY:" + _priority).orElse("")
); );
} }
private final static String TAG = "VEVENT";
} }
@@ -23,16 +23,16 @@
* The main classes and modules in this package include: * The main classes and modules in this package include:
* <ul> * <ul>
* <li> * <li>
* {@link cn.org.codecrafters.webcal.WebCalendar}: A class for * {@link com.onixbyte.webcal.WebCalendar}: A class for
* generating web calendars with customisable settings and events. * generating web calendars with customisable settings and events.
* </li> * </li>
* <li> * <li>
* {@link cn.org.codecrafters.webcal.WebCalendarEvent}: A class * {@link com.onixbyte.webcal.impl.WebCalendarEvent}: A class
* representing a single event in a web calendar with various * representing a single event in a web calendar with various
* attributes and options. * attributes and options.
* </li> * </li>
* <li> * <li>
* {@link cn.org.codecrafters.webcal.WebCalendarNode}: An abstract * {@link com.onixbyte.webcal.WebCalendarNode}: An abstract
* class serving as the base class for web calendar nodes, providing * class serving as the base class for web calendar nodes, providing
* common attributes and functionality for events. * common attributes and functionality for events.
* </li> * </li>
@@ -40,4 +40,4 @@
* *
* @since 1.0.0 * @since 1.0.0
*/ */
package cn.org.codecrafters.webcal; package com.onixbyte.webcal;
@@ -15,11 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.org.codecrafters.webcal.test; package com.onixbyte.webcal.test;
import cn.org.codecrafters.webcal.WebCalendar; import com.onixbyte.webcal.WebCalendar;
import cn.org.codecrafters.webcal.WebCalendarEvent; import com.onixbyte.webcal.impl.WebCalendarEvent;
import cn.org.codecrafters.webcal.config.Classification; import com.onixbyte.webcal.config.Classification;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;