feat(simple-jwt-facade): optimised design of the facade

added TokenAlgorithm enum; added annotation which to exclude some data
which is not proper to be displayed in a JSON Web Token
This commit is contained in:
Zihlu Wang
2023-07-30 22:46:47 +08:00
parent 6c90c69129
commit bfc5d58896
9 changed files with 409 additions and 118 deletions
@@ -1,55 +0,0 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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 cn.org.codecrafters.simplejwt;
/**
* ResolvedToken - Generic Record for Holding Resolved Tokens.
* <p>
* This class represents a generic record that holds a resolved token of type {@code T}. It is used as a simple
* container to store the resolved token value for further processing.
* <p>
* <b>Usage:</b>
* To create a new instance of {@code ResolvedToken}, use the static factory method {@code init}.
*
* @param <T> the type of the resolved token
* @author Zihlu Wang
* @since 1.0.0
*/
public record ResolvedToken<T>(T resolvedToken) {
/**
* Creates a new {@code ResolvedToken} instance with the provided {@code resolvedToken} value.
*
* @param resolvedToken the resolved token value to be stored in the {@code ResolvedToken}
*/
public ResolvedToken(T resolvedToken) {
this.resolvedToken = resolvedToken;
}
/**
* Factory method to create a new {@code ResolvedToken} instance with the provided {@code resolvedToken} value.
*
* @param <T> the type of the resolved token
* @param resolvedToken the resolved token value to be stored in the {@code ResolvedToken}
* @return a new {@code ResolvedToken} instance containing the provided {@code resolvedToken}
*/
public static <T> ResolvedToken<T> init(T resolvedToken) {
return new ResolvedToken<>(resolvedToken);
}
}
@@ -21,8 +21,6 @@ import java.time.Duration;
import java.util.Map; import java.util.Map;
/** /**
* TokenResolver - Interface for Creating, Extracting, and Renewing Tokens.
* <p>
* The {@code TokenResolver} interface defines methods for creating, * The {@code TokenResolver} interface defines methods for creating,
* extracting, and renewing tokens, particularly JSON Web Tokens (JWTs). It * extracting, and renewing tokens, particularly JSON Web Tokens (JWTs). It
* provides a set of methods to generate tokens with various payload * provides a set of methods to generate tokens with various payload
@@ -44,54 +42,24 @@ import java.util.Map;
* expired token by providing a new expiration time, audience, subject, and * expired token by providing a new expiration time, audience, subject, and
* optional custom payload data. * optional custom payload data.
* *
* @param <ResolvedTokenType> the type of the result obtained by the
* third-party library when parsing JWTs
* @author Zihlu Wang
* @version 1.0.0 * @version 1.0.0
* @since 1.0.0 * @since 1.0.0
*/ */
public interface TokenResolver { public interface TokenResolver<ResolvedTokenType> {
/** /**
* Creates a new token with the specified expiration time and audience. * Creates a new token with the specified expiration time, subject, and
* * audience.
* @param expireAfter the duration after which the token will expire
* @param audience the audience for which the token is intended
* @return the generated token as a {@code String}
*/
String createToken(Duration expireAfter, String audience);
/**
* Creates a new token with the specified expiration time, audience, and
* custom payload data.
*
* @param expireAfter the duration after which the token will expire
* @param audience the audience for which the token is intended
* @param payloads the custom payload data to be included in the token
* @return the generated token as a {@code String}
*/
String createToken(Duration expireAfter, String audience, Map<String, Object> payloads);
/**
* Creates a new token with the specified expiration time, audience, and
* strongly-typed payload data.
*
* @param <T> the type of the payload data, must implement
* {@link TokenPayload}
* @param expireAfter the duration after which the token will expire
* @param audience the audience for which the token is intended
* @param payloads the strongly-typed payload data to be included in the
* token
* @return the generated token as a {@code String}
*/
<T extends TokenPayload> String createToken(Duration expireAfter, String audience, T payloads);
/**
* Creates a new token with the specified expiration time, subject, and audience.
* *
* @param expireAfter the duration after which the token will expire * @param expireAfter the duration after which the token will expire
* @param subject the subject of the token * @param subject the subject of the token
* @param audience the audience for which the token is intended * @param audience the audience for which the token is intended
* @return the generated token as a {@code String} * @return the generated token as a {@code String}
*/ */
String createToken(Duration expireAfter, String subject, String audience); String createToken(Duration expireAfter, String audience, String subject);
/** /**
* Creates a new token with the specified expiration time, subject, * Creates a new token with the specified expiration time, subject,
@@ -103,7 +71,7 @@ public interface TokenResolver {
* @param payloads the custom payload data to be included in the token * @param payloads the custom payload data to be included in the token
* @return the generated token as a {@code String} * @return the generated token as a {@code String}
*/ */
String createToken(Duration expireAfter, String subject, String audience, Map<String, Object> payloads); String createToken(Duration expireAfter, String audience, String subject, Map<String, Object> payloads);
/** /**
* Creates a new token with the specified expiration time, subject, * Creates a new token with the specified expiration time, subject,
@@ -114,19 +82,19 @@ public interface TokenResolver {
* @param expireAfter the duration after which the token will expire * @param expireAfter the duration after which the token will expire
* @param subject the subject of the token * @param subject the subject of the token
* @param audience the audience for which the token is intended * @param audience the audience for which the token is intended
* @param payloads the strongly-typed payload data to be included in the * @param payload the strongly-typed payload data to be included in the
* token * token
* @return the generated token as a {@code String} * @return the generated token as a {@code String}
*/ */
<T extends TokenPayload> String createToken(Duration expireAfter, String subject, String audience, T payloads); <T extends TokenPayload> String createToken(Duration expireAfter, String audience, String subject, T payload);
/** /**
* Extracts the payload information from the given token. * Resolves the given token into a ResolvedTokenType object.
* *
* @param token the token from which to extract the payload * @param token the token to be resolved
* @return a map containing the extracted payload data * @return a ResolvedTokenType object
*/ */
Map<String, Object> extract(String token); ResolvedTokenType resolve(String token);
/** /**
* Extracts the payload information from the given token and maps it to the * Extracts the payload information from the given token and maps it to the
@@ -142,22 +110,25 @@ public interface TokenResolver {
<T extends TokenPayload> T extract(String token, Class<T> targetType); <T extends TokenPayload> T extract(String token, Class<T> targetType);
/** /**
* Renews the given expired token. * Renews the given expired token with the specified custom payload data.
* *
* @param oldToken the expired token to be renewed * @param oldToken the expired token to be renewed
* @param payload the custom payload data to be included in the renewed
* token
* @return the renewed token as a {@code String} * @return the renewed token as a {@code String}
*/ */
String renew(String oldToken); String renew(String oldToken, Map<String, Object> payload);
/** /**
* Renews the given expired token with the specified custom payload data. * Renews the given expired token with the specified custom payload data.
* *
* @param oldToken the expired token to be renewed * @param oldToken the expired token to be renewed
* @param payloads the custom payload data to be included in the renewed * @param expireAfter specify when does the new token invalid
* @param payload the custom payload data to be included in the renewed
* token * token
* @return the renewed token as a {@code String} * @return the renewed token as a {@code String}
*/ */
String renew(String oldToken, Map<String, Object> payloads); String renew(String oldToken, Duration expireAfter, Map<String, Object> payload);
/** /**
* Renews the given expired token with the specified strongly-typed * Renews the given expired token with the specified strongly-typed
@@ -166,19 +137,24 @@ public interface TokenResolver {
* @param <T> the type of the payload data, must implement * @param <T> the type of the payload data, must implement
* {@link TokenPayload} * {@link TokenPayload}
* @param oldToken the expired token to be renewed * @param oldToken the expired token to be renewed
* @param payloads the strongly-typed payload data to be included in the * @param expireAfter specify when does the new token invalid
* @param payload the strongly-typed payload data to be included in the
* renewed token * renewed token
* @return the renewed token as a {@code String} * @return the renewed token as a {@code String}
*/ */
<T extends TokenPayload> String renew(String oldToken, T payloads); <T extends TokenPayload> String renew(String oldToken, Duration expireAfter, T payload);
/** /**
* Resolves the given token into a {@link ResolvedToken} object. * Renews the given expired token with the specified strongly-typed
* payload data.
* *
* @param <T> the type of the resolved token * @param <T> the type of the payload data, must implement
* @param token the token to be resolved * {@link TokenPayload}
* @return a {@link ResolvedToken} object containing the resolved token * @param oldToken the expired token to be renewed
* @param payload the strongly-typed payload data to be included in the
* renewed token
* @return the renewed token as a {@code String}
*/ */
<T> ResolvedToken<T> resolve(String token); <T extends TokenPayload> String renew(String oldToken, T payload);
} }
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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 cn.org.codecrafters.simplejwt.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <h3>ExcludeFromPayload</h3>
* <h4>Annotation to Exclude Property from JWT Payload.</h4>
* <p>
* This annotation is used to mark a property of a data class that should be
* excluded from being automatically injected into the JSON Web Token (JWT)
* payload during token generation. When a property is annotated with this
* annotation, it will not be included as part of the JWT payload.
*
* <p><b>Usage:</b>
* To exclude a property from the JWT payload, annotate the property with
* {@code @ExcludeFromPayload}:
*
* <pre>{@code
* public class UserData implements TokenPayload {
* private String username;
*
* // This property will not be included in the JWT payload
* {@literal @}ExcludeFromPayload
* private String sensitiveData;
*
* // Getters and setters...
* }
* }</pre>
*
* <p><b>Note:</b>
* This annotation should be used only on properties that are not intended to
* be included in the JWT payload due to their sensitive nature or for other
* reasons. It is important to carefully choose which properties are excluded
* from the payload to ensure the JWT remains secure and efficient.
*
* @since 1.0.0
* @version 1.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ExcludeFromPayload {
}
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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.
*/
/**
* This package contains annotation classes that are used to prevent annotated
* properties from being automatically injected into the JSON Web Token (JWT)
* payload during token generation. These annotations can be applied to
* properties of a data class to exclude them from being included as part
* of the JWT payload.
*
* @version 1.0.0
* @since 1.0.0
*/
package cn.org.codecrafters.simplejwt.annotations;
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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 cn.org.codecrafters.simplejwt.config;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
/**
* The TokenResolverConfig interface provides a mechanism to configure a
* TokenResolver with algorithm functions.
* <p>
* This generic interface is used to define the configuration details for a
* TokenResolver that utilizes algorithm functions. The interface allows for
* specifying algorithm functions corresponding to different TokenAlgorithm
* instances supported by the TokenResolver implementation.
*
* @param <AlgorithmFunction> the type representing algorithm functions used by
* the TokenResolver
* @author Zihlu Wang
* @version 1.0.0
* @since 1.0.0
*/
public interface TokenResolverConfig<AlgorithmFunction> {
/**
* Gets the algorithm function corresponding to the specified
* TokenAlgorithm.
* <p>
* This method returns the algorithm function associated with the given
* TokenAlgorithm. The provided TokenAlgorithm represents the specific
* algorithm for which the corresponding algorithm function is required.
* The returned AlgorithmFunction represents the function implementation
* that can be used by the TokenResolver to handle the specific algorithm.
*
* @param algorithm the TokenAlgorithm for which the algorithm function is
* required
* @return the algorithm function associated with the given TokenAlgorithm
*/
AlgorithmFunction getFunction(TokenAlgorithm algorithm);
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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 cn.org.codecrafters.simplejwt.constants;
import java.util.List;
/**
* PredefinedKeys
*
* @author Zihlu Wang
*/
public final class PredefinedKeys {
public static final List<String> KEYS = List.of("aud", "sub", "nbf", "iss", "exp", "iat", "jti");
}
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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 cn.org.codecrafters.simplejwt.constants;
import ch.qos.logback.core.subst.Token;
import lombok.Getter;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* The {@code TokenAlgorithm} enum class defines the algorithms that can be
* used for signing and verifying JSON Web Tokens (JWT). JWT allows various
* cryptographic algorithms to be used for secure token generation and
* validation. This enum provides a list of supported algorithms to ensure
* consistent usage and avoid potential security issues.
*
* <p><b>Supported Algorithms:</b>
* This enum includes the following supported algorithms:
* <ul>
* <li>{@link TokenAlgorithm#HS256}: HMAC SHA-256</li>
* <li>{@link TokenAlgorithm#HS384}: HMAC SHA-384</li>
* <li>{@link TokenAlgorithm#HS512}: HMAC SHA-512</li>
* <li>{@link TokenAlgorithm#RS256}: RSA PKCS#1 v1.5 with SHA-256</li>
* <li>{@link TokenAlgorithm#RS384}: RSA PKCS#1 v1.5 with SHA-384</li>
* <li>{@link TokenAlgorithm#RS512}: RSA PKCS#1 v1.5 with SHA-512</li>
* <li>{@link TokenAlgorithm#ES256}: ECDSA with SHA-256</li>
* <li>{@link TokenAlgorithm#ES384}: ECDSA with SHA-384</li>
* <li>{@link TokenAlgorithm#ES512}: ECDSA with SHA-512</li>
* </ul>
*
* @since 1.0.0
* @version 1.0.0
*/
@Getter
public enum TokenAlgorithm {
HS256,
HS384,
HS512,
RS256,
RS384,
RS512,
ES256,
ES384,
ES512,
;
}
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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.
*/
/**
* This package contains constant values related to JSON Web Token (JWT)
* processing. These constants define various aspects of JWT, such as the
* algorithms being used for token signing and verification, and other
* configuration parameters.
*
* @since 1.0.0
* @version 1.0.0
*/
package cn.org.codecrafters.simplejwt.constants;
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* 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 cn.org.codecrafters.simplejwt.exceptions;
/**
* This {@code UnsupportedAlgorithmException} is to indicates that the given
* algorithm is not supported by TokenResolver yet.
* <p>
* To support a specified algorithm, you could
*
* @author Zihlu Wang
*/
public class UnsupportedAlgorithmException extends RuntimeException {
/**
* Constructs a new runtime exception with {@code null} as its
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*/
public UnsupportedAlgorithmException() {
}
/**
* Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public UnsupportedAlgorithmException(String message) {
super(message);
}
/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public UnsupportedAlgorithmException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new runtime exception with the specified cause and a
* detail message of {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of
* {@code cause}). This constructor is useful for runtime exceptions
* that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public UnsupportedAlgorithmException(Throwable cause) {
super(cause);
}
/**
* Constructs a new runtime exception with the specified detail
* message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
* @since 1.7
*/
public UnsupportedAlgorithmException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}