From bfc5d588961186079187fe3e95a58064a29285cc Mon Sep 17 00:00:00 2001 From: Zihlu Wang Date: Sun, 30 Jul 2023 22:46:47 +0800 Subject: [PATCH] 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 --- .../codecrafters/simplejwt/ResolvedToken.java | 55 ---------- .../codecrafters/simplejwt/TokenResolver.java | 102 +++++++----------- .../annotations/ExcludeFromPayload.java | 62 +++++++++++ .../simplejwt/annotations/package-info.java | 28 +++++ .../simplejwt/config/TokenResolverConfig.java | 55 ++++++++++ .../simplejwt/constants/PredefinedKeys.java | 31 ++++++ .../simplejwt/constants/TokenAlgorithm.java | 65 +++++++++++ .../simplejwt/constants/package-info.java | 27 +++++ .../UnsupportedAlgorithmException.java | 102 ++++++++++++++++++ 9 files changed, 409 insertions(+), 118 deletions(-) delete mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/ResolvedToken.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/ExcludeFromPayload.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/package-info.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/config/TokenResolverConfig.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/PredefinedKeys.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/TokenAlgorithm.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/package-info.java create mode 100644 simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/exceptions/UnsupportedAlgorithmException.java diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/ResolvedToken.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/ResolvedToken.java deleted file mode 100644 index adfd19f..0000000 --- a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/ResolvedToken.java +++ /dev/null @@ -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. - *

- * 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. - *

- * Usage: - * To create a new instance of {@code ResolvedToken}, use the static factory method {@code init}. - * - * @param the type of the resolved token - * @author Zihlu Wang - * @since 1.0.0 - */ -public record ResolvedToken(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 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 ResolvedToken init(T resolvedToken) { - return new ResolvedToken<>(resolvedToken); - } - -} diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/TokenResolver.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/TokenResolver.java index b5d8f31..3b93d7c 100644 --- a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/TokenResolver.java +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/TokenResolver.java @@ -21,8 +21,6 @@ import java.time.Duration; import java.util.Map; /** - * TokenResolver - Interface for Creating, Extracting, and Renewing Tokens. - *

* The {@code TokenResolver} interface defines methods for creating, * extracting, and renewing tokens, particularly JSON Web Tokens (JWTs). It * 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 * optional custom payload data. * + * @param the type of the result obtained by the + * third-party library when parsing JWTs + * @author Zihlu Wang * @version 1.0.0 * @since 1.0.0 */ -public interface TokenResolver { +public interface TokenResolver { /** - * Creates a new token with the specified expiration time 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 payloads); - - /** - * Creates a new token with the specified expiration time, audience, and - * strongly-typed payload data. - * - * @param 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} - */ - String createToken(Duration expireAfter, String audience, T payloads); - - /** - * Creates a new token with the specified expiration time, subject, 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 subject the subject of the token * @param audience the audience for which the token is intended * @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, @@ -103,7 +71,7 @@ public interface TokenResolver { * @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 subject, String audience, Map payloads); + String createToken(Duration expireAfter, String audience, String subject, Map payloads); /** * 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 subject the subject of the token * @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 * @return the generated token as a {@code String} */ - String createToken(Duration expireAfter, String subject, String audience, T payloads); + 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 - * @return a map containing the extracted payload data + * @param token the token to be resolved + * @return a ResolvedTokenType object */ - Map extract(String token); + ResolvedTokenType resolve(String token); /** * Extracts the payload information from the given token and maps it to the @@ -142,22 +110,39 @@ public interface TokenResolver { T extract(String token, Class 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 payload the custom payload data to be included in the renewed + * token * @return the renewed token as a {@code String} */ - String renew(String oldToken); + String renew(String oldToken, Map payload); /** * Renews the given expired token with the specified custom payload data. * - * @param oldToken the expired token to be renewed - * @param payloads the custom payload data to be included in the renewed - * token + * @param oldToken the expired token to be renewed + * @param expireAfter specify when does the new token invalid + * @param payload the custom payload data to be included in the renewed + * token * @return the renewed token as a {@code String} */ - String renew(String oldToken, Map payloads); + String renew(String oldToken, Duration expireAfter, Map payload); + + /** + * Renews the given expired token with the specified strongly-typed + * payload data. + * + * @param the type of the payload data, must implement + * {@link TokenPayload} + * @param oldToken the expired token to be renewed + * @param expireAfter specify when does the new token invalid + * @param payload the strongly-typed payload data to be included in the + * renewed token + * @return the renewed token as a {@code String} + */ + String renew(String oldToken, Duration expireAfter, T payload); /** * Renews the given expired token with the specified strongly-typed @@ -166,19 +151,10 @@ public interface TokenResolver { * @param the type of the payload data, must implement * {@link TokenPayload} * @param oldToken the expired token to be renewed - * @param payloads the strongly-typed payload data to be included in the + * @param payload the strongly-typed payload data to be included in the * renewed token * @return the renewed token as a {@code String} */ - String renew(String oldToken, T payloads); - - /** - * Resolves the given token into a {@link ResolvedToken} object. - * - * @param the type of the resolved token - * @param token the token to be resolved - * @return a {@link ResolvedToken} object containing the resolved token - */ - ResolvedToken resolve(String token); + String renew(String oldToken, T payload); } diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/ExcludeFromPayload.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/ExcludeFromPayload.java new file mode 100644 index 0000000..e774597 --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/ExcludeFromPayload.java @@ -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; + +/** + *

ExcludeFromPayload

+ *

Annotation to Exclude Property from JWT Payload.

+ *

+ * 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. + * + *

Usage: + * To exclude a property from the JWT payload, annotate the property with + * {@code @ExcludeFromPayload}: + * + *

{@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...
+ * }
+ * }
+ * + *

Note: + * 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 { +} diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/package-info.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/package-info.java new file mode 100644 index 0000000..86f3e22 --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/annotations/package-info.java @@ -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; \ No newline at end of file diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/config/TokenResolverConfig.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/config/TokenResolverConfig.java new file mode 100644 index 0000000..3b4473c --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/config/TokenResolverConfig.java @@ -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. + *

+ * 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 the type representing algorithm functions used by + * the TokenResolver + * @author Zihlu Wang + * @version 1.0.0 + * @since 1.0.0 + */ +public interface TokenResolverConfig { + + /** + * Gets the algorithm function corresponding to the specified + * TokenAlgorithm. + *

+ * 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); + +} diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/PredefinedKeys.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/PredefinedKeys.java new file mode 100644 index 0000000..4f0ac16 --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/PredefinedKeys.java @@ -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 KEYS = List.of("aud", "sub", "nbf", "iss", "exp", "iat", "jti"); + +} diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/TokenAlgorithm.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/TokenAlgorithm.java new file mode 100644 index 0000000..6254a64 --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/TokenAlgorithm.java @@ -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. + * + *

Supported Algorithms: + * This enum includes the following supported algorithms: + *

+ * + * @since 1.0.0 + * @version 1.0.0 + */ +@Getter +public enum TokenAlgorithm { + HS256, + HS384, + HS512, + RS256, + RS384, + RS512, + ES256, + ES384, + ES512, + ; + +} diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/package-info.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/package-info.java new file mode 100644 index 0000000..192ea9d --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/constants/package-info.java @@ -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; \ No newline at end of file diff --git a/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/exceptions/UnsupportedAlgorithmException.java b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/exceptions/UnsupportedAlgorithmException.java new file mode 100644 index 0000000..54d14a5 --- /dev/null +++ b/simple-jwt-facade/src/main/java/cn/org/codecrafters/simplejwt/exceptions/UnsupportedAlgorithmException.java @@ -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. + *

+ * 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.

Note that the detail message associated with + * {@code cause} is not 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); + } +}