docs: added Javadoc
This commit is contained in:
@@ -18,10 +18,29 @@
|
|||||||
package com.onixbyte.jwt;
|
package com.onixbyte.jwt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Interface for creating and signing JSON Web Tokens (JWTs).
|
||||||
|
* <p>
|
||||||
|
* Defines a contract for implementations that generate signed JWTs from a given payload. The
|
||||||
|
* resulting token is typically a string in the format "header.payload.signature", where the
|
||||||
|
* signature is created using a cryptographic algorithm specific to the implementation.
|
||||||
*
|
*
|
||||||
|
* @author zihluwang
|
||||||
*/
|
*/
|
||||||
public interface TokenCreator {
|
public interface TokenCreator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a token payload to create a JWT.
|
||||||
|
* <p>
|
||||||
|
* Takes a {@link TokenPayload} object, serialises its claims, and generates a signed
|
||||||
|
* JWT string. The specific signing algorithm (e.g., HMAC, RSA, ECDSA) depends on
|
||||||
|
* the implementation.
|
||||||
|
*
|
||||||
|
* @param payload the {@link TokenPayload} containing claims to include in the token
|
||||||
|
* @return the signed JWT as a string in the format "header.payload.signature"
|
||||||
|
* @throws IllegalArgumentException if the payload cannot be serialised to JSON due to invalid
|
||||||
|
* data or structure, or if the signing process fails due to
|
||||||
|
* configuration issues
|
||||||
|
*/
|
||||||
String sign(TokenPayload payload);
|
String sign(TokenPayload payload);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2025 OnixByte.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.onixbyte.jwt;
|
package com.onixbyte.jwt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for managing JSON Web Tokens (JWTs) with support for signing, verification, and
|
||||||
|
* payload extraction.
|
||||||
|
* <p>
|
||||||
|
* Combines the functionality of {@link TokenCreator} for creating signed JWTs and
|
||||||
|
* {@link TokenResolver} for verifying and parsing them, while adding the ability to extract the
|
||||||
|
* payload as a custom type {@code T}. Implementations are expected to handle both token generation
|
||||||
|
* and resolution, providing a unified interface for JWT operations.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object to which the token payload will be converted
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
public interface TokenManager<T> extends TokenCreator, TokenResolver {
|
public interface TokenManager<T> extends TokenCreator, TokenResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the payload from a JWT and converts it to an object of type {@code T}.
|
||||||
|
* <p>
|
||||||
|
* Retrieves the payload from the token and transforms it into the specified type using an
|
||||||
|
* implementation-specific mechanism, such as an adapter or mapper.
|
||||||
|
*
|
||||||
|
* @param token the JWT string from which to extract the payload
|
||||||
|
* @return the payload converted to an object of type {@code T}
|
||||||
|
* @throws IllegalArgumentException if the token is malformed, the signature is invalid, or the
|
||||||
|
* payload cannot be deserialised or converted to
|
||||||
|
* type {@code T}
|
||||||
|
*/
|
||||||
T extract(String token);
|
T extract(String token);
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -24,57 +24,158 @@ import java.time.ZoneId;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A builder-style class for constructing JSON Web Token (JWT) payloads.
|
||||||
|
* <p>
|
||||||
|
* Provides a fluent interface to set standard registered claims (e.g., subject, issuer, audience)
|
||||||
|
* and custom claims for a JWT payload. The class supports chaining method calls to build the
|
||||||
|
* payload incrementally, which can then be retrieved as a map for use in JWT creation. Ensures that
|
||||||
|
* registered claims are set using dedicated methods to prevent misuse.
|
||||||
*
|
*
|
||||||
|
* @author zihluwang
|
||||||
*/
|
*/
|
||||||
public class TokenPayload {
|
public class TokenPayload {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link TokenPayload} with an empty payload.
|
||||||
|
* <p>
|
||||||
|
* Initialises the payload with empty collections for claims and audiences, ready for
|
||||||
|
* configuration via the builder methods.
|
||||||
|
*
|
||||||
|
* @return a new {@link TokenPayload} instance
|
||||||
|
*/
|
||||||
public static TokenPayload createPayload() {
|
public static TokenPayload createPayload() {
|
||||||
return new TokenPayload();
|
return new TokenPayload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map storing custom claims for the JWT payload.
|
||||||
|
*/
|
||||||
private final Map<String, Object> payload;
|
private final Map<String, Object> payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of audience identifiers for the JWT.
|
||||||
|
*/
|
||||||
private final List<String> audiences;
|
private final List<String> audiences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subject of the JWT, identifying the principal.
|
||||||
|
*/
|
||||||
private String subject;
|
private String subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The issuer of the JWT, identifying the entity that issued the token.
|
||||||
|
*/
|
||||||
private String issuer;
|
private String issuer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique identifier for the JWT.
|
||||||
|
*/
|
||||||
private String tokenId;
|
private String tokenId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The expiration time of the JWT, as seconds since the Unix epoch.
|
||||||
|
*/
|
||||||
private Long expiresAt;
|
private Long expiresAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time before which the JWT must not be accepted, as seconds since the Unix epoch.
|
||||||
|
*/
|
||||||
private Long notBefore;
|
private Long notBefore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The issuance time of the JWT, as seconds since the Unix epoch.
|
||||||
|
*/
|
||||||
private Long issuedAt;
|
private Long issuedAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor to prevent instantiation of this utility class.
|
* Private constructor to enforce use of the factory method.
|
||||||
|
* <p>
|
||||||
|
* Initialises the internal collections for storing claims and audiences, preventing direct
|
||||||
|
* instantiation outside the class.
|
||||||
*/
|
*/
|
||||||
private TokenPayload() {
|
private TokenPayload() {
|
||||||
payload = new HashMap<>();
|
payload = new HashMap<>();
|
||||||
audiences = new ArrayList<>();
|
audiences = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single audience to the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Appends the specified audience identifier to the list of audiences, allowing the token to be
|
||||||
|
* validated for multiple recipients.
|
||||||
|
*
|
||||||
|
* @param audience the audience identifier to add
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withAudience(String audience) {
|
public TokenPayload withAudience(String audience) {
|
||||||
audiences.add(audience);
|
audiences.add(audience);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple audiences to the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Appends all provided audience identifiers to the list of audiences, enabling the token to be
|
||||||
|
* validated for multiple recipients.
|
||||||
|
*
|
||||||
|
* @param audiences the audience identifiers to add
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withAudiences(String... audiences) {
|
public TokenPayload withAudiences(String... audiences) {
|
||||||
this.audiences.addAll(Arrays.asList(audiences));
|
this.audiences.addAll(Arrays.asList(audiences));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the subject of the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Specifies the principal that is the subject of the token, typically identifying the user or
|
||||||
|
* entity the token represents.
|
||||||
|
*
|
||||||
|
* @param subject the subject identifier
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withSubject(String subject) {
|
public TokenPayload withSubject(String subject) {
|
||||||
this.subject = subject;
|
this.subject = subject;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the issuer of the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Specifies the entity that issued the token, allowing recipients to verify the token's origin.
|
||||||
|
*
|
||||||
|
* @param issuer the issuer identifier
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withIssuer(String issuer) {
|
public TokenPayload withIssuer(String issuer) {
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the unique identifier for the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Assigns a unique token ID to the JWT, which can be used to prevent token reuse or for
|
||||||
|
* tracking purposes.
|
||||||
|
*
|
||||||
|
* @param tokenId the unique token identifier
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withTokenId(String tokenId) {
|
public TokenPayload withTokenId(String tokenId) {
|
||||||
this.tokenId = tokenId;
|
this.tokenId = tokenId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the expiration time for the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Specifies when the token expires, converted to seconds since the Unix epoch based on the
|
||||||
|
* system's default time zone.
|
||||||
|
*
|
||||||
|
* @param expiresAt the expiration time as a {@link LocalDateTime}
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withExpiresAt(LocalDateTime expiresAt) {
|
public TokenPayload withExpiresAt(LocalDateTime expiresAt) {
|
||||||
this.expiresAt = expiresAt.atZone(ZoneId.systemDefault())
|
this.expiresAt = expiresAt.atZone(ZoneId.systemDefault())
|
||||||
.toInstant()
|
.toInstant()
|
||||||
@@ -82,6 +183,15 @@ public class TokenPayload {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the time before which the JWT must not be accepted.
|
||||||
|
* <p>
|
||||||
|
* Specifies the "not before" time, converted to seconds since the Unix epoch based on the
|
||||||
|
* system's default time zone.
|
||||||
|
*
|
||||||
|
* @param notBefore the time before which the token is invalid, as a {@link LocalDateTime}
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withNotBefore(LocalDateTime notBefore) {
|
public TokenPayload withNotBefore(LocalDateTime notBefore) {
|
||||||
this.notBefore = notBefore.atZone(ZoneId.systemDefault())
|
this.notBefore = notBefore.atZone(ZoneId.systemDefault())
|
||||||
.toInstant()
|
.toInstant()
|
||||||
@@ -89,6 +199,15 @@ public class TokenPayload {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the issuance time for the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Specifies when the token was issued, converted to seconds since the Unix epoch based on the
|
||||||
|
* system's default time zone.
|
||||||
|
*
|
||||||
|
* @param issuedAt the issuance time as a {@link LocalDateTime}
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
*/
|
||||||
public TokenPayload withIssuedAt(LocalDateTime issuedAt) {
|
public TokenPayload withIssuedAt(LocalDateTime issuedAt) {
|
||||||
this.issuedAt = issuedAt.atZone(ZoneId.systemDefault())
|
this.issuedAt = issuedAt.atZone(ZoneId.systemDefault())
|
||||||
.toInstant()
|
.toInstant()
|
||||||
@@ -96,6 +215,17 @@ public class TokenPayload {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a custom claim to the JWT payload.
|
||||||
|
* <p>
|
||||||
|
* Stores a custom key-value pair in the payload, provided the key is not a registered claim.
|
||||||
|
* Registered claims must be set using their dedicated methods to ensure proper handling.
|
||||||
|
*
|
||||||
|
* @param name the name of the custom claim
|
||||||
|
* @param value the value of the custom claim
|
||||||
|
* @return this {@link TokenPayload} instance for method chaining
|
||||||
|
* @throws IllegalStateException if the claim name is a registered claim
|
||||||
|
*/
|
||||||
public TokenPayload withClaim(String name, String value) {
|
public TokenPayload withClaim(String name, String value) {
|
||||||
if (RegisteredClaims.VALUES.contains(name)) {
|
if (RegisteredClaims.VALUES.contains(name)) {
|
||||||
throw new IllegalStateException("Please set registered claims with pre-defined methods");
|
throw new IllegalStateException("Please set registered claims with pre-defined methods");
|
||||||
@@ -105,10 +235,26 @@ public class TokenPayload {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the JWT payload has a valid issuer.
|
||||||
|
* <p>
|
||||||
|
* Returns {@code true} if the issuer is non-null and not blank, indicating that an issuer has
|
||||||
|
* been set.
|
||||||
|
*
|
||||||
|
* @return {@code true} if an issuer is set, {@code false} otherwise
|
||||||
|
*/
|
||||||
public boolean hasIssuer() {
|
public boolean hasIssuer() {
|
||||||
return Objects.nonNull(issuer) && !issuer.isBlank();
|
return Objects.nonNull(issuer) && !issuer.isBlank();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the complete JWT payload as a map.
|
||||||
|
* <p>
|
||||||
|
* Constructs a map containing all custom claims, registered claims (if set), and audiences.
|
||||||
|
* Only non-empty or non-blank values are included to ensure a clean payload.
|
||||||
|
*
|
||||||
|
* @return a map containing the JWT payload
|
||||||
|
*/
|
||||||
public Map<String, Object> getPayload() {
|
public Map<String, Object> getPayload() {
|
||||||
var _payload = new HashMap<>(payload);
|
var _payload = new HashMap<>(payload);
|
||||||
|
|
||||||
@@ -128,7 +274,7 @@ public class TokenPayload {
|
|||||||
.ifPresent((jti) -> _payload.put(RegisteredClaims.TOKEN_ID, jti));
|
.ifPresent((jti) -> _payload.put(RegisteredClaims.TOKEN_ID, jti));
|
||||||
|
|
||||||
Optional.ofNullable(issuer)
|
Optional.ofNullable(issuer)
|
||||||
.map((iss) -> !iss.isBlank())
|
.filter((iss) -> !iss.isBlank())
|
||||||
.ifPresent((iss) -> _payload.put(RegisteredClaims.ISSUER, iss));
|
.ifPresent((iss) -> _payload.put(RegisteredClaims.ISSUER, iss));
|
||||||
|
|
||||||
Optional.ofNullable(issuedAt)
|
Optional.ofNullable(issuedAt)
|
||||||
@@ -139,5 +285,4 @@ public class TokenPayload {
|
|||||||
|
|
||||||
return _payload;
|
return _payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,16 +23,23 @@ import com.onixbyte.jwt.data.RawTokenComponent;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Interface for resolving and verifying JSON Web Tokens (JWTs).
|
||||||
|
* <p>
|
||||||
|
* Defines a contract for implementations that parse, verify, and extract components from JWTs.
|
||||||
|
* Provides methods to validate the token's signature, retrieve its header and payload, and split it
|
||||||
|
* into raw components. Implementations are expected to handle cryptographic verification and JSON
|
||||||
|
* deserialisation specific to their signing algorithm.
|
||||||
*
|
*
|
||||||
|
* @author zihluwang
|
||||||
*/
|
*/
|
||||||
public interface TokenResolver {
|
public interface TokenResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies the HMAC signature of the provided JWT.
|
* Verifies the signature of the provided JWT.
|
||||||
* <p>
|
* <p>
|
||||||
* Splits the token into its components and uses the configured algorithm and secret to check
|
* Splits the token into its components and checks the signature's validity using the
|
||||||
* the signature's validity. If the signature does not match, an exception is thrown by the
|
* implementation's configured algorithm and key. If the signature does not match, an exception
|
||||||
* underlying cryptographic utility.
|
* is thrown.
|
||||||
*
|
*
|
||||||
* @param token the JWT string to verify
|
* @param token the JWT string to verify
|
||||||
* @throws IllegalArgumentException if the token is malformed or the signature verification
|
* @throws IllegalArgumentException if the token is malformed or the signature verification
|
||||||
@@ -68,6 +75,9 @@ public interface TokenResolver {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits a JWT into its raw components: header, payload, and signature.
|
* Splits a JWT into its raw components: header, payload, and signature.
|
||||||
|
* <p>
|
||||||
|
* Provides a default implementation that separates the token string into its three parts using
|
||||||
|
* dot separators and returns them as a {@link RawTokenComponent}.
|
||||||
*
|
*
|
||||||
* @param token the JWT string to split
|
* @param token the JWT string to split
|
||||||
* @return a {@link RawTokenComponent} containing the header, payload, and signature as strings
|
* @return a {@link RawTokenComponent} containing the header, payload, and signature as strings
|
||||||
|
|||||||
@@ -18,7 +18,14 @@
|
|||||||
package com.onixbyte.jwt.constant;
|
package com.onixbyte.jwt.constant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Enumeration of cryptographic algorithms supported for JSON Web Token (JWT) signing
|
||||||
|
* and verification.
|
||||||
|
* <p>
|
||||||
|
* Defines a set of recognised algorithms including HMAC (HS*), RSA (RS*), and ECDSA (ES*) variants,
|
||||||
|
* each with a specific SHA length (256, 384, or 512 bits). Provides methods to identify the
|
||||||
|
* algorithm type and retrieve its properties.
|
||||||
*
|
*
|
||||||
|
* @author zihluwang
|
||||||
*/
|
*/
|
||||||
public enum Algorithm {
|
public enum Algorithm {
|
||||||
HS256(1, 256, "HmacSHA256"),
|
HS256(1, 256, "HmacSHA256"),
|
||||||
@@ -32,32 +39,41 @@ public enum Algorithm {
|
|||||||
ES512(3, 512, "SHA512withECDSA");
|
ES512(3, 512, "SHA512withECDSA");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Bit flag indicating an HMAC-based algorithm.
|
||||||
*/
|
*/
|
||||||
private static final int HS_FLAG = 1; // 001
|
private static final int HS_FLAG = 1; // 001
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Bit flag indicating an RSA-based algorithm.
|
||||||
*/
|
*/
|
||||||
private static final int RS_FLAG = 2; // 010
|
private static final int RS_FLAG = 2; // 010
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Bit flag indicating an ECDSA-based algorithm.
|
||||||
*/
|
*/
|
||||||
private static final int ES_FLAG = 3; // 011
|
private static final int ES_FLAG = 3; // 011
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* The type flag identifying the algorithm family (HMAC, RSA, or ECDSA).
|
||||||
*/
|
*/
|
||||||
private final int typeFlag;
|
private final int typeFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the SHA hash in bits (256, 384, or 512).
|
||||||
|
*/
|
||||||
private final int shaLength;
|
private final int shaLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard name of the algorithm as recognised by the Java Cryptography Architecture (JCA).
|
||||||
|
*/
|
||||||
private final String algorithm;
|
private final String algorithm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Constructs an algorithm enum constant with the specified type flag, SHA length, and algorithm name.
|
||||||
*
|
*
|
||||||
* @param typeFlag
|
* @param typeFlag the bit flag identifying the algorithm type
|
||||||
* @param shaLength
|
* @param shaLength the length of the SHA hash in bits
|
||||||
* @param algorithm
|
* @param algorithm the JCA-compliant algorithm name
|
||||||
*/
|
*/
|
||||||
Algorithm(int typeFlag, int shaLength, String algorithm) {
|
Algorithm(int typeFlag, int shaLength, String algorithm) {
|
||||||
this.typeFlag = typeFlag;
|
this.typeFlag = typeFlag;
|
||||||
@@ -66,48 +82,54 @@ public enum Algorithm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Determines whether this algorithm is HMAC-based.
|
||||||
*
|
*
|
||||||
* @return
|
* @return {@code true} if the algorithm uses HMAC (e.g., HS256, HS384, HS512), {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isHmac() {
|
public boolean isHmac() {
|
||||||
return (this.typeFlag & HS_FLAG) != 0;
|
return (this.typeFlag & HS_FLAG) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Determines whether this algorithm is RSA-based.
|
||||||
*
|
*
|
||||||
* @return
|
* @return {@code true} if the algorithm uses RSA (e.g., RS256, RS384, RS512), {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isRsa() {
|
public boolean isRsa() {
|
||||||
return (this.typeFlag & RS_FLAG) != 0;
|
return (this.typeFlag & RS_FLAG) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Determines whether this algorithm is ECDSA-based.
|
||||||
*
|
*
|
||||||
* @return
|
* @return {@code true} if the algorithm uses ECDSA (e.g., ES256, ES384, ES512), {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isEcdsa() {
|
public boolean isEcdsa() {
|
||||||
return (this.typeFlag & ES_FLAG) != 0;
|
return (this.typeFlag & ES_FLAG) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Retrieves the SHA length of this algorithm in bits.
|
||||||
*
|
*
|
||||||
* @return
|
* @return the SHA length (256, 384, or 512)
|
||||||
*/
|
*/
|
||||||
public int getShaLength() {
|
public int getShaLength() {
|
||||||
return shaLength;
|
return shaLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Retrieves the type flag of this algorithm.
|
||||||
*
|
*
|
||||||
* @return
|
* @return the type flag (1 for HMAC, 2 for RSA, 3 for ECDSA)
|
||||||
*/
|
*/
|
||||||
public int getTypeFlag() {
|
public int getTypeFlag() {
|
||||||
return typeFlag;
|
return typeFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Retrieves the JCA-compliant name of this algorithm.
|
||||||
*
|
*
|
||||||
* @return
|
* @return the algorithm name (e.g., "HmacSHA256", "SHA256withRSA")
|
||||||
*/
|
*/
|
||||||
public String getAlgorithm() {
|
public String getAlgorithm() {
|
||||||
return algorithm;
|
return algorithm;
|
||||||
|
|||||||
@@ -17,6 +17,15 @@
|
|||||||
|
|
||||||
package com.onixbyte.jwt.constant;
|
package com.onixbyte.jwt.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class defining standard header claim names for JSON Web Tokens (JWTs).
|
||||||
|
* <p>
|
||||||
|
* Provides constants representing the recognised header claims as specified in the JWT standard.
|
||||||
|
* These claims are used in the header section of a JWT to describe its structure and cryptographic
|
||||||
|
* properties.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
public final class HeaderClaims {
|
public final class HeaderClaims {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,14 @@ package com.onixbyte.jwt.constant;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Utility class defining standard registered claim names for JSON Web Tokens (JWTs).
|
||||||
|
* <p>
|
||||||
|
* Provides constants representing the registered claims as defined in RFC 7519. These claims are
|
||||||
|
* used in the payload section of a JWT to convey metadata about the token, such as its issuer,
|
||||||
|
* subject, and validity period. All claims are optional but widely recognised in
|
||||||
|
* JWT implementations.
|
||||||
*
|
*
|
||||||
|
* @author zihluwang
|
||||||
*/
|
*/
|
||||||
public final class RegisteredClaims {
|
public final class RegisteredClaims {
|
||||||
|
|
||||||
@@ -73,6 +80,13 @@ public final class RegisteredClaims {
|
|||||||
*/
|
*/
|
||||||
public static final String TOKEN_ID = "jti";
|
public static final String TOKEN_ID = "jti";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable list of all registered claim names defined in this class.
|
||||||
|
* <p>
|
||||||
|
* Contains the values of {@link #ISSUER}, {@link #SUBJECT}, {@link #AUDIENCE},
|
||||||
|
* {@link #EXPIRES_AT}, {@link #NOT_BEFORE}, {@link #ISSUED_AT}, and {@link #TOKEN_ID} for
|
||||||
|
* convenient iteration or lookup.
|
||||||
|
*/
|
||||||
public static final List<String> VALUES = List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRES_AT, NOT_BEFORE, ISSUED_AT, TOKEN_ID);
|
public static final List<String> VALUES = List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRES_AT, NOT_BEFORE, ISSUED_AT, TOKEN_ID);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,15 @@
|
|||||||
package com.onixbyte.jwt.data;
|
package com.onixbyte.jwt.data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A record representing the raw components of a JSON Web Token (JWT).
|
||||||
|
* <p>
|
||||||
|
* Holds the header, payload, and signature of a JWT as strings, typically in their Base64 URL-encoded
|
||||||
|
* form as extracted from a token string. This record is used to facilitate parsing and processing
|
||||||
|
* of JWTs without decoding or validating their contents.
|
||||||
*
|
*
|
||||||
* @param header
|
* @param header the Base64 URL-encoded header string of the JWT
|
||||||
* @param payload
|
* @param payload the Base64 URL-encoded payload string of the JWT
|
||||||
* @param signature
|
* @param signature the Base64 URL-encoded signature string of the JWT
|
||||||
* @author zihluwang
|
* @author zihluwang
|
||||||
*/
|
*/
|
||||||
public record RawTokenComponent(
|
public record RawTokenComponent(
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton holder for a configured {@link ObjectMapper} instance.
|
||||||
|
* <p>
|
||||||
|
* Provides a thread-safe, lazily initialised singleton to manage a single {@link ObjectMapper}
|
||||||
|
* instance, configured to sort JSON properties alphabetically. This class is designed to ensure
|
||||||
|
* consistent JSON serialisation and deserialisation across the application, particularly for
|
||||||
|
* JSON Web Token (JWT) processing.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
public class ObjectMapperHolder {
|
public class ObjectMapperHolder {
|
||||||
|
|
||||||
private static ObjectMapperHolder HOLDER;
|
private static ObjectMapperHolder HOLDER;
|
||||||
@@ -39,9 +49,12 @@ public class ObjectMapperHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get singleton instance.
|
* Retrieves the singleton instance of this holder.
|
||||||
|
* <p>
|
||||||
|
* Uses double-checked locking to ensure thread-safe, lazy initialisation of the singleton. If
|
||||||
|
* the instance has not been created, it is initialised in a synchronised block.
|
||||||
*
|
*
|
||||||
* @return the {@code ObjectMapperHolder} instance
|
* @return the singleton {@link ObjectMapperHolder} instance
|
||||||
*/
|
*/
|
||||||
public static ObjectMapperHolder getInstance() {
|
public static ObjectMapperHolder getInstance() {
|
||||||
if (Objects.isNull(HOLDER)) {
|
if (Objects.isNull(HOLDER)) {
|
||||||
@@ -55,9 +68,9 @@ public class ObjectMapperHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Object
|
* Retrieves the configured {@link ObjectMapper} instance.
|
||||||
*
|
*
|
||||||
* @return
|
* @return the {@link ObjectMapper} configured for alphabetical property sorting
|
||||||
*/
|
*/
|
||||||
public ObjectMapper getObjectMapper() {
|
public ObjectMapper getObjectMapper() {
|
||||||
return objectMapper;
|
return objectMapper;
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2025 OnixByte.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.onixbyte.jwt.impl;
|
package com.onixbyte.jwt.impl;
|
||||||
|
|
||||||
import com.onixbyte.devkit.utils.MapUtil;
|
import com.onixbyte.devkit.utils.MapUtil;
|
||||||
@@ -10,39 +27,116 @@ import com.onixbyte.jwt.constant.Algorithm;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic token manager implementation for creating, verifying, and extracting data from
|
||||||
|
* HMAC-signed JSON Web Tokens (JWTs).
|
||||||
|
* <p>
|
||||||
|
* This class integrates an {@link HmacTokenCreator} for signing tokens and an
|
||||||
|
* {@link HmacTokenResolver} for verification and parsing, using a specified HMAC algorithm. It
|
||||||
|
* supports converting token payloads to a custom type {@code T} via an {@link ObjectMapAdapter}.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object to which the token payload will be converted
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
public class HmacTokenManager<T> implements TokenManager<T> {
|
public class HmacTokenManager<T> implements TokenManager<T> {
|
||||||
|
|
||||||
private final TokenCreator tokenCreator;
|
private final TokenCreator tokenCreator;
|
||||||
private final TokenResolver tokenResolver;
|
private final TokenResolver tokenResolver;
|
||||||
private final ObjectMapAdapter<T> adapter;
|
private final ObjectMapAdapter<T> adapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an HMAC token manager with the specified algorithm, issuer, secret, and adapter.
|
||||||
|
* <p>
|
||||||
|
* Initialises the {@link TokenCreator} and {@link TokenResolver} with the provided HMAC
|
||||||
|
* algorithm and secret key, and associates an adapter for converting token payloads to the
|
||||||
|
* generic type {@code T}.
|
||||||
|
*
|
||||||
|
* @param algorithm the HMAC algorithm to use for signing and verification
|
||||||
|
* @param issuer the issuer identifier to include in the token payload if not already present
|
||||||
|
* @param secret the secret key as a string, used to sign and verify the HMAC signature
|
||||||
|
* @param adapter the {@link ObjectMapAdapter} for converting payload maps to objects of
|
||||||
|
* type {@code T}
|
||||||
|
* @throws IllegalArgumentException if the secret key is too short for the specified algorithm
|
||||||
|
*/
|
||||||
public HmacTokenManager(Algorithm algorithm, String issuer, String secret, ObjectMapAdapter<T> adapter) {
|
public HmacTokenManager(Algorithm algorithm, String issuer, String secret, ObjectMapAdapter<T> adapter) {
|
||||||
this.tokenCreator = new HmacTokenCreator(algorithm, issuer, secret);
|
this.tokenCreator = new HmacTokenCreator(algorithm, issuer, secret);
|
||||||
this.tokenResolver = new HmacTokenResolver(algorithm, secret);
|
this.tokenResolver = new HmacTokenResolver(algorithm, secret);
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the payload from a JWT and converts it to an object of type {@code T}.
|
||||||
|
* <p>
|
||||||
|
* Retrieves the payload as a map and uses the configured {@link ObjectMapAdapter} to transform
|
||||||
|
* it into the desired type.
|
||||||
|
*
|
||||||
|
* @param token the JWT string from which to extract the payload
|
||||||
|
* @return the payload converted to an object of type {@code T}
|
||||||
|
* @throws IllegalArgumentException if the token is malformed, the signature is invalid, or the
|
||||||
|
* payload cannot be deserialised
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public T extract(String token) {
|
public T extract(String token) {
|
||||||
var payloadMap = getPayload(token);
|
var payloadMap = getPayload(token);
|
||||||
return MapUtil.mapToObject(payloadMap, adapter);
|
return MapUtil.mapToObject(payloadMap, adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a token payload to create a JWT.
|
||||||
|
* <p>
|
||||||
|
* Delegates to the {@link TokenCreator} to generate a signed JWT string from the
|
||||||
|
* provided payload.
|
||||||
|
*
|
||||||
|
* @param payload the {@link TokenPayload} containing claims to include in the token
|
||||||
|
* @return the signed JWT as a string in the format "header.payload.signature"
|
||||||
|
* @throws IllegalArgumentException if the payload cannot be serialised to JSON due to invalid
|
||||||
|
* data or structure
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String sign(TokenPayload payload) {
|
public String sign(TokenPayload payload) {
|
||||||
return tokenCreator.sign(payload);
|
return tokenCreator.sign(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the validity of a JWT.
|
||||||
|
* <p>
|
||||||
|
* Delegates to the {@link TokenResolver} to check the token's signature and structure.
|
||||||
|
*
|
||||||
|
* @param token the JWT string to verify
|
||||||
|
* @throws IllegalArgumentException if the token is malformed or the signature
|
||||||
|
* verification fails
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void verify(String token) {
|
public void verify(String token) {
|
||||||
tokenResolver.verify(token);
|
tokenResolver.verify(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the header claims from the provided JWT.
|
||||||
|
* <p>
|
||||||
|
* Decodes the Base64-encoded header and deserialises it into a map of strings.
|
||||||
|
*
|
||||||
|
* @param token the JWT string from which to extract the header
|
||||||
|
* @return a map containing the header claims as key-value pairs
|
||||||
|
* @throws IllegalArgumentException if the token is malformed or the header cannot be
|
||||||
|
* deserialised due to invalid JSON format
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getHeader(String token) {
|
public Map<String, String> getHeader(String token) {
|
||||||
return tokenResolver.getHeader(token);
|
return tokenResolver.getHeader(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payload claims from a JWT.
|
||||||
|
* <p>
|
||||||
|
* Delegates to the {@link TokenResolver} to extract and deserialise the payload into a map,
|
||||||
|
* excluding registered claims.
|
||||||
|
*
|
||||||
|
* @param token the JWT string from which to extract the payload
|
||||||
|
* @return a map containing the custom payload claims as key-value pairs
|
||||||
|
* @throws IllegalArgumentException if the token is malformed or the payload cannot
|
||||||
|
* be deserialised
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getPayload(String token) {
|
public Map<String, Object> getPayload(String token) {
|
||||||
return tokenResolver.getPayload(token);
|
return tokenResolver.getPayload(token);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.onixbyte.jwt.TokenResolver;
|
import com.onixbyte.jwt.TokenResolver;
|
||||||
import com.onixbyte.jwt.constant.Algorithm;
|
import com.onixbyte.jwt.constant.Algorithm;
|
||||||
import com.onixbyte.jwt.constant.RegisteredClaims;
|
import com.onixbyte.jwt.constant.RegisteredClaims;
|
||||||
import com.onixbyte.jwt.data.RawTokenComponent;
|
|
||||||
import com.onixbyte.jwt.holder.ObjectMapperHolder;
|
import com.onixbyte.jwt.holder.ObjectMapperHolder;
|
||||||
import com.onixbyte.jwt.util.CryptoUtil;
|
import com.onixbyte.jwt.util.CryptoUtil;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user