Merge pull request #35 from OnixByte/feature/key-loader
Release of 1.6.0
This commit is contained in:
@@ -34,8 +34,7 @@ import java.util.Objects;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AesUtil} can help you encrypt and decrypt data with specified secret
|
* {@link AesUtil} can help you encrypt and decrypt data with specified secret by AES algorithm.
|
||||||
* by AES algorithm.
|
|
||||||
*
|
*
|
||||||
* @author hubin@baomidou
|
* @author hubin@baomidou
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
@@ -81,8 +80,9 @@ public final class AesUtil {
|
|||||||
var cipher = Cipher.getInstance(AES_CBC_CIPHER);
|
var cipher = Cipher.getInstance(AES_CBC_CIPHER);
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
|
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
|
||||||
return cipher.doFinal(data);
|
return cipher.doFinal(data);
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedOperationException |
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException |
|
||||||
InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException |
|
UnsupportedOperationException | InvalidKeyException |
|
||||||
|
InvalidAlgorithmParameterException | IllegalBlockSizeException |
|
||||||
BadPaddingException exception) {
|
BadPaddingException exception) {
|
||||||
log.error(exception.getMessage());
|
log.error(exception.getMessage());
|
||||||
for (var stackTraceElement : exception.getStackTrace()) {
|
for (var stackTraceElement : exception.getStackTrace()) {
|
||||||
@@ -100,7 +100,8 @@ public final class AesUtil {
|
|||||||
* @return the encryption result or {@code null} if encryption failed
|
* @return the encryption result or {@code null} if encryption failed
|
||||||
*/
|
*/
|
||||||
public static String encrypt(String data, String secret) {
|
public static String encrypt(String data, String secret) {
|
||||||
return Base64.getEncoder().encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8), secret.getBytes(StandardCharsets.UTF_8)));
|
return Base64.getEncoder().encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8),
|
||||||
|
secret.getBytes(StandardCharsets.UTF_8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import java.util.Objects;
|
|||||||
* provided. It is recommended to specify the charset explicitly to ensure consistent
|
* provided. It is recommended to specify the charset explicitly to ensure consistent
|
||||||
* encoding and decoding.
|
* encoding and decoding.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -293,7 +293,9 @@ public final class ChainedCalcUtil {
|
|||||||
* or null if not applicable
|
* or null if not applicable
|
||||||
* @return a ChainedCalcUtil instance with the updated value
|
* @return a ChainedCalcUtil instance with the updated value
|
||||||
*/
|
*/
|
||||||
private ChainedCalcUtil operator(BiFunction<BigDecimal, BigDecimal, BigDecimal> operator, Object other, Integer beforeOperateScale) {
|
private ChainedCalcUtil operator(BiFunction<BigDecimal, BigDecimal, BigDecimal> operator,
|
||||||
|
Object other,
|
||||||
|
Integer beforeOperateScale) {
|
||||||
return baseOperator((otherValue) ->
|
return baseOperator((otherValue) ->
|
||||||
operator.apply(this.value, otherValue),
|
operator.apply(this.value, otherValue),
|
||||||
other,
|
other,
|
||||||
@@ -311,7 +313,8 @@ public final class ChainedCalcUtil {
|
|||||||
* @return a ChainedCalcUtil instance with the updated value
|
* @return a ChainedCalcUtil instance with the updated value
|
||||||
*/
|
*/
|
||||||
private synchronized ChainedCalcUtil baseOperator(Function<BigDecimal, BigDecimal> operatorFunction,
|
private synchronized ChainedCalcUtil baseOperator(Function<BigDecimal, BigDecimal> operatorFunction,
|
||||||
Object anotherValue, Integer beforeOperateScale) {
|
Object anotherValue,
|
||||||
|
Integer beforeOperateScale) {
|
||||||
if (Objects.isNull(anotherValue)) {
|
if (Objects.isNull(anotherValue)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ import java.util.Optional;
|
|||||||
* for data integrity checks and password storage, but they should not be used for
|
* for data integrity checks and password storage, but they should not be used for
|
||||||
* encryption purposes.
|
* encryption purposes.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @see java.security.MessageDigest
|
* @see java.security.MessageDigest
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ import java.util.Optional;
|
|||||||
* Note: Since version 1.4.2, this util class removed reflection API and transferred to a safer API.
|
* Note: Since version 1.4.2, this util class removed reflection API and transferred to a safer API.
|
||||||
* Please see documentation for more information.
|
* Please see documentation for more information.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.4.2
|
* @version 1.4.2
|
||||||
* @see com.onixbyte.devkit.utils.unsafe.ReflectMapUtil
|
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
|||||||
+3
-3
@@ -15,9 +15,9 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
jacksonVersion=2.17.0
|
jacksonVersion=2.17.2
|
||||||
javaJwtVersion=4.4.0
|
javaJwtVersion=4.4.0
|
||||||
jjwtVersion=0.12.5
|
jjwtVersion=0.12.6
|
||||||
junitVersion=5.10.2
|
junitVersion=5.10.2
|
||||||
logbackVersion=1.5.4
|
logbackVersion=1.5.4
|
||||||
lombokVersion=1.18.30
|
lombokVersion=1.18.30
|
||||||
@@ -26,7 +26,7 @@ springVersion=6.1.3
|
|||||||
springBootVersion=3.2.3
|
springBootVersion=3.2.3
|
||||||
|
|
||||||
buildGroupId=com.onixbyte
|
buildGroupId=com.onixbyte
|
||||||
buildVersion=1.5.0
|
buildVersion=1.6.0
|
||||||
projectUrl=https://onixbyte.com/JDevKit
|
projectUrl=https://onixbyte.com/JDevKit
|
||||||
projectGithubUrl=https://github.com/OnixByte/JDevKit
|
projectGithubUrl=https://github.com/OnixByte/JDevKit
|
||||||
licenseName=The Apache License, Version 2.0
|
licenseName=The Apache License, Version 2.0
|
||||||
|
|||||||
@@ -24,20 +24,11 @@ val projectGithubUrl: String by project
|
|||||||
val licenseName: String by project
|
val licenseName: String by project
|
||||||
val licenseUrl: String by project
|
val licenseUrl: String by project
|
||||||
|
|
||||||
val jacksonVersion: String by project
|
|
||||||
val jjwtVersion: String by project
|
|
||||||
|
|
||||||
group = buildGroupId
|
group = buildGroupId
|
||||||
version = buildVersion
|
version = buildVersion
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":devkit-utils"))
|
implementation(project(":devkit-core"))
|
||||||
implementation(project(":guid"))
|
|
||||||
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
|
||||||
implementation("io.jsonwebtoken:jjwt-api:$jjwtVersion")
|
|
||||||
implementation("io.jsonwebtoken:jjwt-impl:$jjwtVersion")
|
|
||||||
implementation("io.jsonwebtoken:jjwt-jackson:$jjwtVersion")
|
|
||||||
implementation(project(":simple-jwt-facade"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -53,14 +44,15 @@ tasks.test {
|
|||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
create<MavenPublication>("simpleJwtJjwt") {
|
create<MavenPublication>("keyPairLoader") {
|
||||||
groupId = buildGroupId
|
groupId = buildGroupId
|
||||||
artifactId = "simple-jwt-jjwt"
|
artifactId = "key-pair-loader"
|
||||||
version = buildVersion
|
version = buildVersion
|
||||||
|
|
||||||
pom {
|
pom {
|
||||||
name = "Simple JWT :: JJWT"
|
name = "Key Pair Loader"
|
||||||
description = "SSimple JWT implemented with io.jsonwebtoken:jjwt."
|
description =
|
||||||
|
"This module can easily load key pairs from a PEM content."
|
||||||
url = projectUrl
|
url = projectUrl
|
||||||
|
|
||||||
licenses {
|
licenses {
|
||||||
@@ -89,7 +81,7 @@ publishing {
|
|||||||
from(components["java"])
|
from(components["java"])
|
||||||
|
|
||||||
signing {
|
signing {
|
||||||
sign(publishing.publications["simpleJwtJjwt"])
|
sign(publishing.publications["keyPairLoader"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2024 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.security;
|
||||||
|
|
||||||
|
import com.onixbyte.security.exception.KeyLoadingException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KeyLoader can load key pairs from PEM formated content.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @version 1.6.0
|
||||||
|
* @since 1.6.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class KeyLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor prevents from being initialised.
|
||||||
|
*/
|
||||||
|
private KeyLoader() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load ECDSA private key from pem-formatted key text.
|
||||||
|
*
|
||||||
|
* @param pemKeyText pem-formatted key text
|
||||||
|
* @return loaded private key
|
||||||
|
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
|
||||||
|
* or EC Key Factory is not loaded, or key spec is invalid
|
||||||
|
*/
|
||||||
|
public static ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) {
|
||||||
|
try {
|
||||||
|
var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
|
||||||
|
var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
|
||||||
|
var keyFactory = KeyFactory.getInstance("EC");
|
||||||
|
var _key = keyFactory.generatePrivate(keySpec);
|
||||||
|
if (_key instanceof ECPrivateKey privateKey) {
|
||||||
|
return privateKey;
|
||||||
|
} else {
|
||||||
|
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new KeyLoadingException("Cannot get EC Key Factory.", e);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
throw new KeyLoadingException("Key spec is invalid.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load ECDSA public key from pem-formatted key text.
|
||||||
|
*
|
||||||
|
* @param pemKeyText pem-formatted key text
|
||||||
|
* @return loaded private key
|
||||||
|
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
|
||||||
|
* or EC Key Factory is not loaded, or key spec is invalid
|
||||||
|
*/
|
||||||
|
public static ECPublicKey loadEcdsaPublicKey(String pemKeyText) {
|
||||||
|
try {
|
||||||
|
var keyBytes = Base64.getDecoder().decode(pemKeyText);
|
||||||
|
var spec = new X509EncodedKeySpec(keyBytes);
|
||||||
|
var keyFactory = KeyFactory.getInstance("EC");
|
||||||
|
var key = keyFactory.generatePublic(spec);
|
||||||
|
if (key instanceof ECPublicKey publicKey) {
|
||||||
|
return publicKey;
|
||||||
|
} else {
|
||||||
|
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new KeyLoadingException("Cannot get EC Key Factory.", e);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
throw new KeyLoadingException("Key spec is invalid.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2024 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.security.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code KeyLoadingException} is an exception indicating an error occurred while loading a key.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @version 1.6.0
|
||||||
|
* @since 1.6.0
|
||||||
|
*/
|
||||||
|
public class KeyLoadingException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code KeyLoadingException} without a specific message or cause.
|
||||||
|
*/
|
||||||
|
public KeyLoadingException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code KeyLoadingException} with the specified detail message.
|
||||||
|
*
|
||||||
|
* @param message the detail message
|
||||||
|
*/
|
||||||
|
public KeyLoadingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code KeyLoadingException} with the specified detail message
|
||||||
|
* and cause.
|
||||||
|
*
|
||||||
|
* @param message the detail message
|
||||||
|
* @param cause the cause of this exception
|
||||||
|
*/
|
||||||
|
public KeyLoadingException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code KeyLoadingException} with the specified cause.
|
||||||
|
*
|
||||||
|
* @param cause the cause of this exception
|
||||||
|
*/
|
||||||
|
public KeyLoadingException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new exception with the specified detail message, cause, suppression enabled
|
||||||
|
* or disabled, and writable stack trace enabled or disabled.
|
||||||
|
*
|
||||||
|
* @param message the detail message
|
||||||
|
* @param cause the cause of this exception
|
||||||
|
* @param enableSuppression whether suppression is enabled or disabled
|
||||||
|
* @param writableStackTrace whether the stack trace should be writable
|
||||||
|
*/
|
||||||
|
public KeyLoadingException(String message,
|
||||||
|
Throwable cause,
|
||||||
|
boolean enableSuppression,
|
||||||
|
boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
~ Copyright (C) 2023 CodeCraftersCN.
|
~ Copyright (C) 2024-2024 OnixByte.
|
||||||
~
|
~
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
~ you may not use this file except in compliance with the License.
|
~ you may not use this file except in compliance with the License.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2024 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.security;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class KeyPairLoaderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2024 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
val buildGroupId: String by project
|
||||||
|
val buildVersion: String by project
|
||||||
|
val projectUrl: String by project
|
||||||
|
val projectGithubUrl: String by project
|
||||||
|
val licenseName: String by project
|
||||||
|
val licenseUrl: String by project
|
||||||
|
|
||||||
|
group = buildGroupId
|
||||||
|
version = buildVersion
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":devkit-core"))
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("mapUtilUnsafe") {
|
||||||
|
groupId = buildGroupId
|
||||||
|
artifactId = "map-util-unsafe"
|
||||||
|
version = buildVersion
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name = "Unsafe Map Util"
|
||||||
|
description =
|
||||||
|
"This module is a converter that can convert an object to a map with unsafe methods."
|
||||||
|
url = projectUrl
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = licenseName
|
||||||
|
url = licenseUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scm {
|
||||||
|
connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
|
||||||
|
developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
|
||||||
|
url = projectGithubUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = "zihluwang"
|
||||||
|
name = "Zihlu Wang"
|
||||||
|
email = "really@zihlu.wang"
|
||||||
|
timezone = "Asia/Hong_Kong"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from(components["java"])
|
||||||
|
|
||||||
|
signing {
|
||||||
|
sign(publishing.publications["mapUtilUnsafe"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "sonatypeNexus"
|
||||||
|
url = URI(providers.gradleProperty("repo.maven-central.host").get())
|
||||||
|
credentials {
|
||||||
|
username = providers.gradleProperty("repo.maven-central.username").get()
|
||||||
|
password = providers.gradleProperty("repo.maven-central.password").get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -53,7 +53,7 @@ public final class ReflectMapUtil {
|
|||||||
var declaredFields = obj.getClass().getDeclaredFields();
|
var declaredFields = obj.getClass().getDeclaredFields();
|
||||||
for (var field : declaredFields) {
|
for (var field : declaredFields) {
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
Object result = field.get(obj);
|
var result = field.get(obj);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
map.put(field.getName(), result);
|
map.put(field.getName(), result);
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,8 @@ public final class ReflectMapUtil {
|
|||||||
* @throws IllegalAccessException if an error occurs while accessing the field
|
* @throws IllegalAccessException if an error occurs while accessing the field
|
||||||
* @throws NoSuchMethodException if the specific setter is not present
|
* @throws NoSuchMethodException if the specific setter is not present
|
||||||
*/
|
*/
|
||||||
public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
public static void setFieldValue(Object obj, String fieldName, Object fieldValue)
|
||||||
|
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
||||||
var objectClass = obj.getClass();
|
var objectClass = obj.getClass();
|
||||||
var methodName = getMethodName("set", fieldName);
|
var methodName = getMethodName("set", fieldName);
|
||||||
var method = objectClass.getDeclaredMethod(methodName, fieldValue.getClass());
|
var method = objectClass.getDeclaredMethod(methodName, fieldValue.getClass());
|
||||||
+2
-3
@@ -20,12 +20,11 @@ rootProject.name = "JDevKit"
|
|||||||
include(
|
include(
|
||||||
"devkit-core",
|
"devkit-core",
|
||||||
"devkit-utils",
|
"devkit-utils",
|
||||||
|
"map-util-unsafe",
|
||||||
"guid",
|
"guid",
|
||||||
"webcal",
|
"key-pair-loader",
|
||||||
"simple-jwt-facade",
|
"simple-jwt-facade",
|
||||||
"simple-jwt-authzero",
|
"simple-jwt-authzero",
|
||||||
"simple-jwt-jjwt",
|
|
||||||
"simple-jwt-spring-boot-starter",
|
"simple-jwt-spring-boot-starter",
|
||||||
"property-guard-spring-boot-starter"
|
"property-guard-spring-boot-starter"
|
||||||
)
|
)
|
||||||
include("map-util-unsafe")
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ version = buildVersion
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":devkit-utils"))
|
implementation(project(":devkit-utils"))
|
||||||
implementation(project(":guid"))
|
implementation(project(":guid"))
|
||||||
|
implementation(project(":key-pair-loader"))
|
||||||
implementation(project(":simple-jwt-facade"))
|
implementation(project(":simple-jwt-facade"))
|
||||||
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
||||||
implementation("com.auth0:java-jwt:$javaJwtVersion")
|
implementation("com.auth0:java-jwt:$javaJwtVersion")
|
||||||
|
|||||||
+200
-101
@@ -19,12 +19,11 @@ package com.onixbyte.simplejwt.authzero;
|
|||||||
|
|
||||||
import com.onixbyte.devkit.utils.Base64Util;
|
import com.onixbyte.devkit.utils.Base64Util;
|
||||||
import com.onixbyte.guid.GuidCreator;
|
import com.onixbyte.guid.GuidCreator;
|
||||||
import com.onixbyte.simplejwt.SecretCreator;
|
import com.onixbyte.security.KeyLoader;
|
||||||
import com.onixbyte.simplejwt.TokenPayload;
|
import com.onixbyte.simplejwt.TokenPayload;
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
import com.onixbyte.simplejwt.TokenResolver;
|
||||||
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
|
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
|
||||||
import com.onixbyte.simplejwt.annotations.TokenEnum;
|
import com.onixbyte.simplejwt.annotations.TokenEnum;
|
||||||
import com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
|
|
||||||
import com.onixbyte.simplejwt.constants.PredefinedKeys;
|
import com.onixbyte.simplejwt.constants.PredefinedKeys;
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
||||||
import com.auth0.jwt.JWT;
|
import com.auth0.jwt.JWT;
|
||||||
@@ -36,24 +35,29 @@ 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.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.onixbyte.simplejwt.exceptions.IllegalKeyPairException;
|
||||||
|
import com.onixbyte.simplejwt.exceptions.IllegalSecretException;
|
||||||
|
import com.onixbyte.simplejwt.exceptions.UnsupportedAlgorithmException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code AuthzeroTokenResolver} class is an implementation of the {@link
|
* The {@code AuthzeroTokenResolver} class is an implementation of the {@link TokenResolver}
|
||||||
* TokenResolver} interface. It uses the {@code
|
* interface. It uses the {@code com.auth0:java-jwt} library to handle JSON Web Token (JWT)
|
||||||
* com.auth0:java-jwt} library to handle JSON Web Token (JWT) resolution. This
|
* resolution. This resolver provides functionality to create, extract, verify, and renew JWT
|
||||||
* resolver provides functionality to create, extract, verify, and renew JWT
|
|
||||||
* tokens using various algorithms and custom payload data.
|
* tokens using various algorithms and custom payload data.
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Usage:</b>
|
* <b>Usage:</b>
|
||||||
* To use the {@code AuthzeroTokenResolver}, first, create an instance of this
|
* To use the {@code AuthzeroTokenResolver}, first, create an instance of this class:
|
||||||
* class:
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* TokenResolver<DecodedJWT> tokenResolver =
|
* TokenResolver<DecodedJWT> tokenResolver =
|
||||||
* new AuthzeroTokenResolver(TokenAlgorithm.HS256,
|
* new AuthzeroTokenResolver(TokenAlgorithm.HS256,
|
||||||
@@ -62,8 +66,7 @@ import java.util.*;
|
|||||||
* "Token Secret");
|
* "Token Secret");
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* <p>
|
* <p>
|
||||||
* Then, you can utilize the various methods provided by this resolver to
|
* Then, you can utilize the various methods provided by this resolver to handle JWT tokens:
|
||||||
* handle JWT tokens:
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* // Creating a new JWT token
|
* // Creating a new JWT token
|
||||||
* String token =
|
* String token =
|
||||||
@@ -82,10 +85,9 @@ import java.util.*;
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Note:</b>
|
* <b>Note:</b>
|
||||||
* It is essential to configure the appropriate algorithms, secret, and issuer
|
* It is essential to configure the appropriate algorithms, secret, and issuer according to your
|
||||||
* according to your specific use case when using this resolver.
|
* specific use case when using this resolver. Additionally, ensure that the
|
||||||
* Additionally, ensure that the {@code com.auth0:java-jwt} library is
|
* {@code com.auth0:java-jwt} library is correctly configured in your project's dependencies.
|
||||||
* correctly configured in your project's dependencies.
|
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author Zihlu Wang
|
||||||
* @version 1.1.1
|
* @version 1.1.1
|
||||||
@@ -100,95 +102,160 @@ import java.util.*;
|
|||||||
public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@code AuthzeroTokenResolver} with the
|
* Create a builder of {@link AuthzeroTokenResolver}.
|
||||||
* provided configurations.
|
|
||||||
*
|
*
|
||||||
* @param jtiCreator the {@link GuidCreator} used for generating unique
|
* @return a builder instance
|
||||||
* identifiers for "jti" claim in JWT tokens
|
|
||||||
* @param algorithm the algorithm used for signing and verifying JWT
|
|
||||||
* tokens
|
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
|
||||||
* HS384, HS512) for token signing and verification
|
|
||||||
* @param objectMapper JSON handler
|
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) {
|
public static Builder builder() {
|
||||||
if (secret == null || secret.isBlank()) {
|
return new Builder();
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for {@link AuthzeroTokenResolver}
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GuidCreator used for generating unique identifiers for "jti" claim in JWT tokens.
|
||||||
|
*/
|
||||||
|
private GuidCreator<?> jtiCreator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The algorithm used for signing and verifying JWT tokens.
|
||||||
|
*/
|
||||||
|
private Algorithm algorithm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The issuer claim value to be included in JWT tokens.
|
||||||
|
*/
|
||||||
|
private String issuer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jackson JSON handler.
|
||||||
|
*/
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The secret to sign a JWT with HMAC based algorithm.
|
||||||
|
*/
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The private key to sign a JWT with ECDSA based algorithm.
|
||||||
|
*/
|
||||||
|
private ECPrivateKey privateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The public key to read a JWT with ECDSA based algorithm.
|
||||||
|
*/
|
||||||
|
private ECPublicKey publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor prevents this class being initialised at somewhere it should not
|
||||||
|
* be initialised.
|
||||||
|
*/
|
||||||
|
private Builder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
* Set the secret to sign a JWT.
|
||||||
|
*
|
||||||
|
* @param secret the secret
|
||||||
|
* @return the builder instance
|
||||||
|
*/
|
||||||
|
public Builder secret(String secret) {
|
||||||
|
this.secret = secret;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.jtiCreator = jtiCreator;
|
/**
|
||||||
this.algorithm = config
|
* Set the key pair to sign a JWT.
|
||||||
.getAlgorithm(algorithm)
|
*
|
||||||
.apply(secret);
|
* @param publicKey the pem formatted public key text
|
||||||
this.issuer = issuer;
|
* @param privateKey the pem formatted private key text
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
* @return the builder instance
|
||||||
this.objectMapper = objectMapper;
|
*/
|
||||||
}
|
public Builder keyPair(String publicKey, String privateKey) {
|
||||||
|
this.publicKey = KeyLoader.loadEcdsaPublicKey(publicKey);
|
||||||
|
this.privateKey = KeyLoader.loadEcdsaPrivateKey(privateKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link AuthzeroTokenResolver} with the
|
* Set the algorithm to sign a JWT.
|
||||||
* provided configurations and a simple UUID GuidCreator.
|
* <p>
|
||||||
*
|
* A secret required by HMAC-based algorithms, or key pair required by ECDSA-based
|
||||||
* @param algorithm the algorithm used for signing and verifying JWT tokens
|
* algorithms need to be set before initialise an algorithm.
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
*
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
* @param algorithm an {@link TokenAlgorithm} value
|
||||||
* HS384, HS512) for token signing and verification
|
* @return the builder instance
|
||||||
* @param objectMapper Jackson Databind JSON Handler
|
*/
|
||||||
*/
|
public Builder algorithm(TokenAlgorithm algorithm) {
|
||||||
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) {
|
// check the secret or key pair before algorithm initialised
|
||||||
this(UUID::randomUUID, algorithm, issuer, secret, objectMapper);
|
if (HMAC_ALGORITHMS.containsKey(algorithm)) {
|
||||||
}
|
if (Objects.isNull(secret) || secret.isBlank()) {
|
||||||
|
throw new IllegalSecretException("""
|
||||||
|
Please specify a secret before define an algorithm.""");
|
||||||
|
}
|
||||||
|
} else if (ECDSA_ALGORITHMS.containsKey(algorithm)) {
|
||||||
|
if (Objects.isNull(publicKey) || Objects.isNull(privateKey)) {
|
||||||
|
throw new IllegalKeyPairException("""
|
||||||
|
Please specify a ECDSA key pair before define an algorithm.""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// initialise algorithm
|
||||||
* Creates a new instance of {@link AuthzeroTokenResolver} with the
|
this.algorithm = switch (algorithm) {
|
||||||
* provided configurations and a simple UUID GuidCreator.
|
case HS256, HS384, HS512 -> HMAC_ALGORITHMS.get(algorithm).apply(secret);
|
||||||
*
|
case ES256, ES384, ES512 -> ECDSA_ALGORITHMS.get(algorithm)
|
||||||
* @param algorithm the algorithm used for signing and verifying JWT tokens
|
.apply(publicKey, privateKey);
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
default -> throw new UnsupportedAlgorithmException("""
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
This algorithm is not supported yet.""");
|
||||||
* HS384, HS512) for token signing and verification
|
};
|
||||||
*/
|
return this;
|
||||||
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret) {
|
}
|
||||||
this(UUID::randomUUID, algorithm, issuer, secret, new ObjectMapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link AuthzeroTokenResolver} with the
|
* Set the object mapper.
|
||||||
* provided configurations, HMAC256 algorithm and a simple
|
*
|
||||||
* UUID GuidCreator.
|
* @param objectMapper an object mapper
|
||||||
*
|
* @return the builder instance
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
*/
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
public Builder objectMapper(ObjectMapper objectMapper) {
|
||||||
* HS384, HS512) for token signing and verification
|
this.objectMapper = objectMapper;
|
||||||
*/
|
return this;
|
||||||
public AuthzeroTokenResolver(String issuer, String secret) {
|
}
|
||||||
this(UUID::randomUUID, TokenAlgorithm.HS256, issuer, secret, new ObjectMapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link AuthzeroTokenResolver} with the
|
* Set the creator of JWT id.
|
||||||
* provided configurations, HMAC256 algorithm and a simple
|
*
|
||||||
* UUID GuidCreator.
|
* @param jtiCreator a creator to create JWT id
|
||||||
*
|
* @return the builder instance
|
||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
*/
|
||||||
*/
|
public Builder jtiCreator(GuidCreator<?> jtiCreator) {
|
||||||
public AuthzeroTokenResolver(String issuer) {
|
this.jtiCreator = jtiCreator;
|
||||||
var secret = SecretCreator.createSecret(32, true, true, true);
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
this.jtiCreator = UUID::randomUUID;
|
/**
|
||||||
this.algorithm = config
|
* Set the issuer of created JWT.
|
||||||
.getAlgorithm(TokenAlgorithm.HS256)
|
*
|
||||||
.apply(secret);
|
* @param issuer the person or organisation issued this JWT
|
||||||
this.issuer = issuer;
|
* @return the builder instance
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
*/
|
||||||
this.objectMapper = new ObjectMapper();
|
public Builder issuer(String issuer) {
|
||||||
|
this.issuer = issuer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
log.info("The secret has been set to {}.", secret);
|
/**
|
||||||
|
* Create an {@link AuthzeroTokenResolver} instance
|
||||||
|
*
|
||||||
|
* @return created instance
|
||||||
|
*/
|
||||||
|
public AuthzeroTokenResolver build() {
|
||||||
|
return new AuthzeroTokenResolver(jtiCreator, algorithm, issuer, objectMapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,7 +306,7 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public <T extends TokenPayload> String createToken(Duration expireAfter, String audience, String subject, T payload) {
|
public <T extends TokenPayload> String createToken(Duration expireAfter, String audience, String subject, T payload) {
|
||||||
final JWTCreator.Builder builder = JWT.create();
|
final var builder = JWT.create();
|
||||||
buildBasicInfo(builder, expireAfter, subject, audience);
|
buildBasicInfo(builder, expireAfter, subject, audience);
|
||||||
|
|
||||||
var payloadClass = payload.getClass();
|
var payloadClass = payload.getClass();
|
||||||
@@ -282,7 +349,7 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public DecodedJWT resolve(String token) {
|
public DecodedJWT resolve(String token) {
|
||||||
return verifier.verify(token);
|
return jwtVerifier.verify(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -438,15 +505,15 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
// bind issuer (iss)
|
// bind issuer (iss)
|
||||||
builder.withIssuer(issuer);
|
builder.withIssuer(issuer);
|
||||||
// bind issued at (iat)
|
// bind issued at (iat)
|
||||||
builder.withIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
|
builder.withIssuedAt(now.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
// bind not before (nbf)
|
// bind not before (nbf)
|
||||||
builder.withNotBefore(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
|
builder.withNotBefore(now.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
// bind audience (aud)
|
// bind audience (aud)
|
||||||
builder.withAudience(audience);
|
builder.withAudience(audience);
|
||||||
// bind subject (sub)
|
// bind subject (sub)
|
||||||
builder.withSubject(subject);
|
builder.withSubject(subject);
|
||||||
// bind expire at (exp)
|
// bind expire at (exp)
|
||||||
builder.withExpiresAt(Date.from(now.plus(expireAfter).atZone(ZoneId.systemDefault()).toInstant()));
|
builder.withExpiresAt(now.plus(expireAfter).atZone(ZoneId.systemDefault()).toInstant());
|
||||||
// bind JWT Id (jti)
|
// bind JWT Id (jti)
|
||||||
builder.withJWTId(jtiCreator.nextId().toString());
|
builder.withJWTId(jtiCreator.nextId().toString());
|
||||||
}
|
}
|
||||||
@@ -522,14 +589,13 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
/**
|
/**
|
||||||
* Default type reference for Map.
|
* Default type reference for Map.
|
||||||
*/
|
*/
|
||||||
private static class MapTypeReference extends TypeReference<Map<String, Object>> {
|
public static class MapTypeReference extends TypeReference<Map<String, Object>> {
|
||||||
MapTypeReference() {
|
MapTypeReference() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GuidCreator used for generating unique identifiers for "jti" claim in
|
* GuidCreator used for generating unique identifiers for "jti" claim in JWT tokens.
|
||||||
* JWT tokens.
|
|
||||||
*/
|
*/
|
||||||
private final GuidCreator<?> jtiCreator;
|
private final GuidCreator<?> jtiCreator;
|
||||||
|
|
||||||
@@ -546,12 +612,45 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
/**
|
/**
|
||||||
* The JSON Web Token resolver.
|
* The JSON Web Token resolver.
|
||||||
*/
|
*/
|
||||||
private final JWTVerifier verifier;
|
private final JWTVerifier jwtVerifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jackson JSON handler.
|
* Jackson JSON handler.
|
||||||
*/
|
*/
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final AuthzeroTokenResolverConfig config = AuthzeroTokenResolverConfig.getInstance();
|
/**
|
||||||
|
* A map contains all HMAC-SHA based algorithms.
|
||||||
|
*/
|
||||||
|
public static final Map<TokenAlgorithm, Function<String, Algorithm>> HMAC_ALGORITHMS = Map.of(
|
||||||
|
TokenAlgorithm.HS256, Algorithm::HMAC256,
|
||||||
|
TokenAlgorithm.HS384, Algorithm::HMAC384,
|
||||||
|
TokenAlgorithm.HS512, Algorithm::HMAC512
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map contains all ECDSA based algorithms.
|
||||||
|
*/
|
||||||
|
public static final Map<TokenAlgorithm, BiFunction<ECPublicKey, ECPrivateKey, Algorithm>> ECDSA_ALGORITHMS = Map.of(
|
||||||
|
TokenAlgorithm.ES256, Algorithm::ECDSA256,
|
||||||
|
TokenAlgorithm.ES384, Algorithm::ECDSA384,
|
||||||
|
TokenAlgorithm.ES512, Algorithm::ECDSA512
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor prevent this class being initialised mistakenly.
|
||||||
|
*
|
||||||
|
* @param jtiCreator a creator that can create JWT id
|
||||||
|
* @param algorithm an algorithm to sign this JWT
|
||||||
|
* @param issuer the person or organisation who issued this JWT
|
||||||
|
* @param objectMapper a mapper for handling JSON serialisation and deserialization
|
||||||
|
*/
|
||||||
|
private AuthzeroTokenResolver(GuidCreator<?> jtiCreator, Algorithm algorithm, String issuer, ObjectMapper objectMapper) {
|
||||||
|
this.jtiCreator = jtiCreator;
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
this.issuer = issuer;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
this.jwtVerifier = JWT.require(algorithm).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-143
@@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.simplejwt.authzero.config;
|
|
||||||
|
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
|
||||||
import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
|
|
||||||
import com.onixbyte.simplejwt.config.TokenResolverConfig;
|
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
|
||||||
import com.onixbyte.simplejwt.exceptions.UnsupportedAlgorithmException;
|
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@code AuthzeroTokenResolverConfig} class provides the configuration for
|
|
||||||
* the {@link AuthzeroTokenResolver}.
|
|
||||||
* <p>
|
|
||||||
* This configuration is used to establish the mapping between the standard
|
|
||||||
* {@link TokenAlgorithm} defined in the
|
|
||||||
* {@code cn.org.codecrafters:simple-jwt-facade} and the specific algorithms
|
|
||||||
* used by the {@code com.auth0:java-jwt} library, which is the underlying
|
|
||||||
* library used by {@link AuthzeroTokenResolver} to handle JSON Web Tokens
|
|
||||||
* (JWTs).
|
|
||||||
* <p>
|
|
||||||
* <b>Algorithm Mapping:</b>
|
|
||||||
* The {@code AuthzeroTokenResolverConfig} allows specifying the relationships
|
|
||||||
* between the standard {@link TokenAlgorithm} instances supported by
|
|
||||||
* {@link AuthzeroTokenResolver} and the corresponding algorithms used by the
|
|
||||||
* {@code com.auth0:java-jwt} library. The mapping is achieved using a Map,
|
|
||||||
* where the keys are the standard {@link TokenAlgorithm} instances, and the
|
|
||||||
* values represent the algorithm functions used by {@code com.auth0:java-jwt}
|
|
||||||
* library for each corresponding key.
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b>
|
|
||||||
* The provided algorithm mapping should be consistent with the actual
|
|
||||||
* algorithms supported and used by the {@code com.auth0:java-jwt} library. It
|
|
||||||
* is crucial to ensure that the mapping is accurate to enable proper token
|
|
||||||
* validation and processing within the {@link AuthzeroTokenResolver}.
|
|
||||||
*
|
|
||||||
* @author Zihlu Wang
|
|
||||||
* @version 1.1.1
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public final class AuthzeroTokenResolverConfig implements TokenResolverConfig<Function<String, Algorithm>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the instance of {@code AuthzeroTokenResolverConfig}.
|
|
||||||
* <p>
|
|
||||||
* This method returns the singleton instance of
|
|
||||||
* {@code AuthzeroTokenResolverConfig}. If the instance is not yet created,
|
|
||||||
* it will create a new instance and return it. Otherwise, it returns the
|
|
||||||
* existing instance.
|
|
||||||
*
|
|
||||||
* @return the instance of {@code AuthzeroTokenResolverConfig}
|
|
||||||
*/
|
|
||||||
public static AuthzeroTokenResolverConfig getInstance() {
|
|
||||||
if (Objects.isNull(instance)) {
|
|
||||||
instance = new AuthzeroTokenResolverConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the algorithm function corresponding to the specified
|
|
||||||
* {@link TokenAlgorithm}.
|
|
||||||
* <p>
|
|
||||||
* This method returns the algorithm function associated with the given
|
|
||||||
* {@link TokenAlgorithm}. The provided {@link TokenAlgorithm} represents
|
|
||||||
* the specific algorithm for which the corresponding algorithm function
|
|
||||||
* is required. The returned Algorithm Function represents the function
|
|
||||||
* implementation that can be used by the {@link TokenResolver} to handle
|
|
||||||
* the specific algorithm.
|
|
||||||
*
|
|
||||||
* @param algorithm the {@link TokenAlgorithm} for which the algorithm
|
|
||||||
* function isrequired
|
|
||||||
* @return the algorithm function associated with the given {@link
|
|
||||||
* TokenAlgorithm}
|
|
||||||
* @throws UnsupportedAlgorithmException if the given {@code algorithm} is
|
|
||||||
* not supported by this
|
|
||||||
* implementation
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Function<String, Algorithm> getAlgorithm(TokenAlgorithm algorithm) {
|
|
||||||
return Optional.of(SUPPORTED_ALGORITHMS).map((entry) -> entry.get(algorithm))
|
|
||||||
.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);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The package {@code cn.org.codecrafters.simplejwt.authzero.config} contains
|
|
||||||
* configuration classes related to the {@link
|
|
||||||
* com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver}
|
|
||||||
* implementation.
|
|
||||||
* <p>
|
|
||||||
* The classes in this package provide configuration options and settings for
|
|
||||||
* the {@link com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver},
|
|
||||||
* which is used for resolving JSON Web Tokens (JWT) using the Auth0 library.
|
|
||||||
* <p>
|
|
||||||
* The {@link
|
|
||||||
* com.onixbyte.simplejwt.authzero.config.AuthzeroTokenResolverConfig}
|
|
||||||
* class is a configuration class that defines the mapping between standard
|
|
||||||
* {@link com.onixbyte.simplejwt.constants.TokenAlgorithm} and the
|
|
||||||
* corresponding function implementation used by {@link
|
|
||||||
* com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver} for handling
|
|
||||||
* JWT algorithms. It enables developers to specify and customize the
|
|
||||||
* algorithm functions according to the chosen JWT algorithm and the library
|
|
||||||
* being used.
|
|
||||||
* <p>
|
|
||||||
* The configuration options in this package help developers integrate and
|
|
||||||
* configure the {@link
|
|
||||||
* com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver} seamlessly
|
|
||||||
* into their Spring Boot applications. Developers can fine-tune the token
|
|
||||||
* resolution process and customize algorithm handling to align with their
|
|
||||||
* specific requirements and desired level of security.
|
|
||||||
* <p>
|
|
||||||
* It is recommended to explore the classes in this package to understand how
|
|
||||||
* to configure and use the {@link
|
|
||||||
* com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver} effectively
|
|
||||||
* in the Spring Boot environment to handle JWT authentication and
|
|
||||||
* authorisation securely and efficiently.
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.onixbyte.simplejwt.authzero.config;
|
|
||||||
-59
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.simplejwt.config;
|
|
||||||
|
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@code TokenResolverConfig} provides a mechanism to configure an
|
|
||||||
* implementation of {@link TokenResolver} with algorithm functions.
|
|
||||||
* <p>
|
|
||||||
* This generic interface is used to define the configuration details for a
|
|
||||||
* {@link TokenResolver} that utilizes algorithm functions. The interface
|
|
||||||
* allows for specifying algorithm functions corresponding to different {@link
|
|
||||||
* TokenAlgorithm} instances supported by the {@link TokenResolver}
|
|
||||||
* implementation.
|
|
||||||
*
|
|
||||||
* @param <Algo> the type representing algorithm functions used by the
|
|
||||||
* {@link TokenResolver}
|
|
||||||
* @author Zihlu Wang
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public interface TokenResolverConfig<Algo> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the algorithm function corresponding to the specified {@link
|
|
||||||
* TokenAlgorithm}.
|
|
||||||
* <p>
|
|
||||||
* This method returns the algorithm function associated with the given
|
|
||||||
* {@link TokenAlgorithm}. The provided TokenAlgorithm represents the
|
|
||||||
* specific algorithm for which the corresponding algorithm function is
|
|
||||||
* required. The returned {@code Algo} represents the function
|
|
||||||
* implementation that can be used by the {@link TokenResolver} to handle
|
|
||||||
* the specific algorithm.
|
|
||||||
*
|
|
||||||
* @param algorithm the {@link TokenAlgorithm} for which the algorithm
|
|
||||||
* function is required
|
|
||||||
* @return the algorithm function associated with the given {@link
|
|
||||||
* TokenAlgorithm}
|
|
||||||
*/
|
|
||||||
Algo getAlgorithm(TokenAlgorithm algorithm);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The classes in this package provide configuration options and settings for
|
|
||||||
* the {@code cn.org.codecrafters:simple-jwt-facade} library. They are used
|
|
||||||
* to customize the behavior of the library and allow developers to fine-tune
|
|
||||||
* various aspects of JWT generation and validation.
|
|
||||||
* <p>
|
|
||||||
* Other configuration classes may be added in the future to support additional
|
|
||||||
* customisation options and features. Developers using the
|
|
||||||
* {@code cn.org.codecrafters:simple-jwt-facade} library should be familiar
|
|
||||||
* with the available configuration options to ensure proper integration and
|
|
||||||
* usage of the library.
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
package com.onixbyte.simplejwt.config;
|
|
||||||
+8
-6
@@ -35,13 +35,14 @@ import java.util.List;
|
|||||||
* <li>{@link #JWT_ID}: Represents the "jti" (JWT ID) claim.</li>
|
* <li>{@link #JWT_ID}: Represents the "jti" (JWT ID) claim.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The class also contains a list of all the standard claim constants, accessible via the {@link #KEYS} field. This
|
* The class also contains a list of all the standard claim constants, accessible via the {@link
|
||||||
* list can be useful for iterating through all the standard claims or checking for the presence of specific claims.
|
* #KEYS} field. This list can be useful for iterating through all the standard claims or checking
|
||||||
|
* for the presence of specific claims.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: This class is final and cannot be instantiated. It only serves as a utility class to hold the standard JWT
|
* Note: This class is final and cannot be instantiated. It only serves as a utility class to hold
|
||||||
* claim constants.
|
* the standard JWT claim constants.
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +86,8 @@ public final class PredefinedKeys {
|
|||||||
/**
|
/**
|
||||||
* List containing all the standard JWT claim constants.
|
* List containing all the standard JWT claim constants.
|
||||||
*/
|
*/
|
||||||
public static final List<String> KEYS = List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID);
|
public static final List<String> KEYS =
|
||||||
|
List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor will protect this class from being instantiated.
|
* Private constructor will protect this class from being instantiated.
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package com.onixbyte.simplejwt.constants;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code TokenAlgorithm} enum class defines the algorithms that can be
|
* The {@code TokenAlgorithm} enum class defines the algorithms that can be
|
||||||
* used for signing and verifying JSON Web Tokens (JWT). JWT allows various
|
* used for signing and verifying JSON Web Tokens (JWT). JWT allows various
|
||||||
@@ -92,4 +94,18 @@ public enum TokenAlgorithm {
|
|||||||
ES512,
|
ES512,
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMAC-based algorithms.
|
||||||
|
*/
|
||||||
|
public static final List<TokenAlgorithm> HMAC_ALGORITHMS = List.of(
|
||||||
|
TokenAlgorithm.HS256, TokenAlgorithm.HS384, TokenAlgorithm.HS512
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ECDSA-based algorithms.
|
||||||
|
*/
|
||||||
|
public static final List<TokenAlgorithm> ECDSA_ALGORITHMS = List.of(
|
||||||
|
TokenAlgorithm.ES256, TokenAlgorithm.ES384, TokenAlgorithm.ES512
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2024 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.simplejwt.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link IllegalKeyPairException} indicates an exception that the key pair is invalid.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @version 1.6.0
|
||||||
|
*/
|
||||||
|
public class IllegalKeyPairException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default exception instance.
|
||||||
|
*/
|
||||||
|
public IllegalKeyPairException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an exception instance with specific message.
|
||||||
|
*
|
||||||
|
* @param message the message of the exception
|
||||||
|
*/
|
||||||
|
public IllegalKeyPairException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an exception instance with specific message and cause.
|
||||||
|
*
|
||||||
|
* @param message the message of the exception
|
||||||
|
* @param cause the cause of the exception
|
||||||
|
*/
|
||||||
|
public IllegalKeyPairException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2024 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.simplejwt.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link IllegalKeyPairException} indicates the secret to sign a JWT is illegal.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @version 1.6.0
|
||||||
|
* @since 1.6.0
|
||||||
|
*/
|
||||||
|
public class IllegalSecretException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default exception instance.
|
||||||
|
*/
|
||||||
|
public IllegalSecretException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an exception instance with specific message.
|
||||||
|
*
|
||||||
|
* @param message the message of the exception
|
||||||
|
*/
|
||||||
|
public IllegalSecretException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an exception instance with specific message and the cause of this exception.
|
||||||
|
*
|
||||||
|
* @param message the message of the exception
|
||||||
|
* @param cause the cause of the exception
|
||||||
|
*/
|
||||||
|
public IllegalSecretException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
# Module `simple-jwt-jjwt`
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
The `simple-jwt-jjwt` module is an implementation of module `simple-jwt-facade` which uses the third-party library [`io.jsonwebtoken:jjwt-api`](https://github.com/jwtk/jjwt). By using this module can help you use JWT to help you in your application.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
This whole `JDevKit` is developed by **JDK 17**, which means you have to use JDK 17 for better experience.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
> This module has already imported `simple-jwt-facade`, there is no need to import it again.
|
|
||||||
|
|
||||||
### If you are using `Maven`
|
|
||||||
|
|
||||||
It is quite simple to install this module by `Maven`. The only thing you need to do is find your `pom.xml` file in the project, then find the `<dependencies>` node in the `<project>` node, and add the following codes to `<dependencies>` node:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.org.codecrafters</groupId>
|
|
||||||
<artifactId>simple-jwt-jjwt</artifactId>
|
|
||||||
<version>${simple-jwt-jjwt.version}</version>
|
|
||||||
</dependency>
|
|
||||||
```
|
|
||||||
|
|
||||||
And run `mvn dependency:get` in your project root folder(i.e., if your `pom.xml` is located at `/path/to/your/project/pom.xml`, then your current work folder should be `/path/to/your/project`), then `Maven` will automatically download the `jar` archive from `Maven Central Repository`. This could be **MUCH EASIER** if you are using IDE(i.e., IntelliJ IDEA), the only thing you need to do is click the refresh button of `Maven`.
|
|
||||||
|
|
||||||
### If you are using `Gradle`
|
|
||||||
|
|
||||||
Add this module to your project with `Gradle` is much easier than doing so with `Maven`.
|
|
||||||
|
|
||||||
Find `build.gradle` in the needed project, and add the following code to the `dependencies` closure in the build script:
|
|
||||||
|
|
||||||
```groovy
|
|
||||||
implementation 'cn.org.codecrafters:simple-jwt-authzero:${simple-jwt-authzero.version}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### If you are not using `Maven` or `Gradle`
|
|
||||||
|
|
||||||
1. Download the `jar` file from the Internet.
|
|
||||||
2. Create a folder in your project and name it as a name you like(i.e., for me, I prefer `vendor`).
|
|
||||||
3. Put the `jar` file to the folder you just created in Step 2.
|
|
||||||
4. Add this folder to your project `classpath`.
|
|
||||||
|
|
||||||
## 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 `jjwt.com.onixbyte.simplejwt.JjwtTokenResolver` and other operations to JWT could follow our instruction in [`simple-jwt-facade`](../simple-jwt-facade/README.md).
|
|
||||||
|
|
||||||
## Contact
|
|
||||||
|
|
||||||
If you have any suggestions, ideas, don't hesitate contacting us via [GitHub Issues](https://github.com/CodeCraftersCN/jdevkit/issues/new) or [Discord Community](https://discord.gg/NQK9tjcBB8).
|
|
||||||
|
|
||||||
If you face any bugs while using our library and you are able to fix any bugs in our library, we would be happy to accept pull requests from you on [GitHub](https://github.com/CodeCraftersCN/jdevkit/compare).
|
|
||||||
@@ -1,480 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.simplejwt.jjwt;
|
|
||||||
|
|
||||||
import com.onixbyte.devkit.utils.MapUtil;
|
|
||||||
import com.onixbyte.guid.GuidCreator;
|
|
||||||
import com.onixbyte.simplejwt.SecretCreator;
|
|
||||||
import com.onixbyte.simplejwt.TokenPayload;
|
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
|
||||||
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
|
|
||||||
import com.onixbyte.simplejwt.annotations.TokenEnum;
|
|
||||||
import com.onixbyte.simplejwt.constants.PredefinedKeys;
|
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
|
||||||
import com.onixbyte.simplejwt.exceptions.WeakSecretException;
|
|
||||||
import com.onixbyte.simplejwt.jjwt.config.JjwtTokenResolverConfig;
|
|
||||||
import io.jsonwebtoken.Claims;
|
|
||||||
import io.jsonwebtoken.Jws;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.security.Keys;
|
|
||||||
import io.jsonwebtoken.security.SecureDigestAlgorithm;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link JjwtTokenResolver} class is an implementation of the {@link
|
|
||||||
* TokenResolver} interface. It uses the {@code
|
|
||||||
* io.jsonwebtoken:jjwt} library to handle JSON Web Token (JWT) resolution.
|
|
||||||
* This resolver provides functionality to create, extract, verify, and renew
|
|
||||||
* JWT tokens using various algorithms and custom payload data.
|
|
||||||
* <p>
|
|
||||||
* <b>Usage:</b>
|
|
||||||
* To use the {@code JjwtTokenResolver}, first, create an instance of this
|
|
||||||
* class:
|
|
||||||
* <pre>{@code
|
|
||||||
* TokenResolver<Jws<Claims>> tokenResolver =
|
|
||||||
* new JjwtTokenResolver(TokenAlgorithm.HS256,
|
|
||||||
* "Token Subject",
|
|
||||||
* "Token Issuer",
|
|
||||||
* "Token Secret");
|
|
||||||
* }</pre>
|
|
||||||
* <p>
|
|
||||||
* Then, you can utilize the various methods provided by this resolver to
|
|
||||||
* handle JWT tokens:
|
|
||||||
* <pre>{@code
|
|
||||||
* // Creating a new JWT token
|
|
||||||
* String token =
|
|
||||||
* tokenResolver.createToken(Duration.ofHours(1),
|
|
||||||
* "your_subject",
|
|
||||||
* "your_audience",
|
|
||||||
* customPayloads);
|
|
||||||
*
|
|
||||||
* // Extracting payload data from a JWT token
|
|
||||||
* DecodedJWT decodedJWT = tokenResolver.resolve(token);
|
|
||||||
* T payloadData = decodedJWT.extract(token, T.class);
|
|
||||||
*
|
|
||||||
* // Renewing an existing JWT token
|
|
||||||
* String renewedToken =
|
|
||||||
* tokenResolver.renew(token, Duration.ofMinutes(30), customPayloads);
|
|
||||||
* }</pre>
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b>
|
|
||||||
* It is essential to configure the appropriate algorithms, secret, and issuer
|
|
||||||
* according to your specific use case when using this resolver.
|
|
||||||
* Additionally, ensure that the {@code io.jsonwebtoken:jjwt} library is
|
|
||||||
* correctly configured in your project's dependencies.
|
|
||||||
*
|
|
||||||
* @author Zihlu Wang
|
|
||||||
* @version 1.1.0
|
|
||||||
* @see Claims
|
|
||||||
* @see Jws
|
|
||||||
* @see Jwts
|
|
||||||
* @see SecureDigestAlgorithm
|
|
||||||
* @see Keys
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a resolver with specified algorithm, issuer, secret and guid strategy.
|
|
||||||
*
|
|
||||||
* @param jtiCreator jwt id creator
|
|
||||||
* @param algorithm specified algorithm
|
|
||||||
* @param issuer specified issuer
|
|
||||||
* @param secret specified secret
|
|
||||||
*/
|
|
||||||
public JjwtTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, String issuer, String secret) {
|
|
||||||
if (Objects.isNull(secret) || secret.isBlank()) {
|
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secret.length() < 32) {
|
|
||||||
log.error("""
|
|
||||||
The provided secret which owns {} characters is too weak. Please replace it with a stronger one.""",
|
|
||||||
secret.length());
|
|
||||||
throw new WeakSecretException("""
|
|
||||||
The provided secret which owns %s characters is too weak. Please replace it with a stronger one."""
|
|
||||||
.formatted(secret.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jtiCreator = jtiCreator;
|
|
||||||
this.algorithm = config.getAlgorithm(algorithm);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a resolver with specified algorithm, issuer, secret and default guid strategy.
|
|
||||||
*
|
|
||||||
* @param algorithm specified algorithm
|
|
||||||
* @param issuer specified issuer
|
|
||||||
* @param secret specified secret
|
|
||||||
*/
|
|
||||||
public JjwtTokenResolver(TokenAlgorithm algorithm, String issuer, String secret) {
|
|
||||||
if (secret == null || secret.isBlank()) {
|
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secret.length() < 32) {
|
|
||||||
log.error(
|
|
||||||
"The provided secret which owns {} characters is too weak. Please replace it with a stronger one.",
|
|
||||||
secret.length());
|
|
||||||
throw new WeakSecretException(
|
|
||||||
"The provided secret which owns %s characters is too weak. Please replace it with a stronger one."
|
|
||||||
.formatted(secret.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jtiCreator = UUID::randomUUID;
|
|
||||||
this.algorithm = config.getAlgorithm(algorithm);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a resolver with specified issuer, secret, default algorithm and guid strategy.
|
|
||||||
*
|
|
||||||
* @param issuer specified issuer
|
|
||||||
* @param secret specified secret
|
|
||||||
* @see #JjwtTokenResolver(TokenAlgorithm, String, String)
|
|
||||||
*/
|
|
||||||
public JjwtTokenResolver(String issuer, String secret) {
|
|
||||||
if (secret == null || secret.isBlank()) {
|
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secret.length() < 32) {
|
|
||||||
log.error(
|
|
||||||
"The provided secret which owns {} characters is too weak. Please replace it with a stronger one.",
|
|
||||||
secret.length());
|
|
||||||
throw new WeakSecretException(
|
|
||||||
"The provided secret which owns %s characters is too weak. Please replace it with a stronger one."
|
|
||||||
.formatted(secret.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jtiCreator = UUID::randomUUID;
|
|
||||||
this.algorithm = config.getAlgorithm(TokenAlgorithm.HS256);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a resolver with specified issuer, random secret string, default algorithm and guid strategy.
|
|
||||||
*
|
|
||||||
* @param issuer specified issuer
|
|
||||||
* @see #JjwtTokenResolver(String, String)
|
|
||||||
*/
|
|
||||||
public JjwtTokenResolver(String issuer) {
|
|
||||||
this.jtiCreator = UUID::randomUUID;
|
|
||||||
this.algorithm = config.getAlgorithm(TokenAlgorithm.HS256);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.key = Keys.hmacShaKeyFor(SecretCreator.createSecret(32, true, true, true).getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @param subject the subject of the token
|
|
||||||
* @return the generated token as a {@code String}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String createToken(Duration expireAfter, String audience, String subject) {
|
|
||||||
return buildToken(expireAfter, audience, subject, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new token with the specified expiration time, subject,
|
|
||||||
* 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 subject the subject of the token
|
|
||||||
* @param payload the custom payload data to be included in the token
|
|
||||||
* @return the generated token as a {@code String}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String createToken(Duration expireAfter, String audience, String subject, Map<String, Object> payload) {
|
|
||||||
return buildToken(expireAfter, audience, subject, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new token with the specified expiration time, subject,
|
|
||||||
* audience, and strongly-typed payload data.
|
|
||||||
*
|
|
||||||
* @param expireAfter the duration after which the token will expire
|
|
||||||
* @param audience the audience for which the token is intended
|
|
||||||
* @param subject the subject of the token
|
|
||||||
* @param payload the strongly-typed payload data to be included in the
|
|
||||||
* token
|
|
||||||
* @return the generated token as a {@code String} or {@code null} if
|
|
||||||
* creation fails
|
|
||||||
* @see MapUtil#objectToMap(Object, Map)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T extends TokenPayload> String createToken(Duration expireAfter, String audience, String subject, T payload) {
|
|
||||||
var fields = payload.getClass().getDeclaredFields();
|
|
||||||
var payloadMap = new HashMap<String, Object>();
|
|
||||||
|
|
||||||
for (var field : fields) {
|
|
||||||
if (field.isAnnotationPresent(ExcludeFromPayload.class))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var getter = payload.getClass().getDeclaredMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
|
|
||||||
// Build Claims
|
|
||||||
/*
|
|
||||||
* Note (17 Oct, 2023): The jjwt can only add a map to be added.
|
|
||||||
*/
|
|
||||||
var fieldValue = getter.invoke(payload);
|
|
||||||
|
|
||||||
// Handle enum fields.
|
|
||||||
if (field.isAnnotationPresent(TokenEnum.class)) {
|
|
||||||
var annotation = field.getAnnotation(TokenEnum.class);
|
|
||||||
var enumGetter = field.getType().getDeclaredMethod("get" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1));
|
|
||||||
fieldValue = enumGetter.invoke(fieldValue);
|
|
||||||
}
|
|
||||||
payloadMap.put(field.getName(), fieldValue);
|
|
||||||
} catch (IllegalAccessException | NoSuchMethodException e) {
|
|
||||||
log.error("Cannot access field {}!", field.getName());
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
log.error("Cannot invoke getter.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildToken(expireAfter, audience, subject, payloadMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves the given token into a {@link Jws<Claims>} object.
|
|
||||||
*
|
|
||||||
* @param token the token to be resolved
|
|
||||||
* @return a ResolvedTokenType object
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Jws<Claims> resolve(String token) {
|
|
||||||
return Jwts.parser()
|
|
||||||
.verifyWith(key)
|
|
||||||
.build()
|
|
||||||
.parseSignedClaims(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the payload information from the given token and maps it to the
|
|
||||||
* specified target type.
|
|
||||||
*
|
|
||||||
* @param token the token from which to extract the payload
|
|
||||||
* @param targetType the target class representing the payload data type
|
|
||||||
* @return an instance of the specified target type with the extracted
|
|
||||||
* payload data, or {@code null} if extraction fails.
|
|
||||||
* @see MapUtil#mapToObject(Map, Object, Map)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T extends TokenPayload> T extract(String token, Class<T> targetType) {
|
|
||||||
var resolvedToken = resolve(token);
|
|
||||||
|
|
||||||
var claims = resolvedToken.getPayload();
|
|
||||||
try {
|
|
||||||
var bean = targetType.getConstructor().newInstance();
|
|
||||||
|
|
||||||
for (var entry : claims.entrySet()) {
|
|
||||||
// Jump all JWT pre-defined properties and the fields that are annotated to be excluded.
|
|
||||||
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var field = targetType.getDeclaredField(entry.getKey());
|
|
||||||
var fieldValue = entry.getValue();
|
|
||||||
if (field.isAnnotationPresent(TokenEnum.class)) {
|
|
||||||
var annotation = field.getAnnotation(TokenEnum.class);
|
|
||||||
var enumStaticLoader = field.getType().getDeclaredMethod("loadBy" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1), annotation.dataType().getMappedClass());
|
|
||||||
fieldValue = enumStaticLoader.invoke(null, entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), fieldValue.getClass());
|
|
||||||
if (setter.canAccess(bean)) {
|
|
||||||
setter.invoke(bean, fieldValue);
|
|
||||||
} else {
|
|
||||||
log.error("Setter for field {} can't be accessed.", entry.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bean;
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
log.error("Target is not invokable.", e);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
log.error("Cannot find method according to given data.", e);
|
|
||||||
} catch (InstantiationException e) {
|
|
||||||
log.error("The required type {} is abstract or an interface.", targetType.getCanonicalName());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
log.error("An error occurs while accessing the fields of the object.", e);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
log.error("Cannot load field according to given field name.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-generate a new token with the payload in the old one.
|
|
||||||
*
|
|
||||||
* @param oldToken the old token
|
|
||||||
* @param expireAfter how long the new token can be valid for
|
|
||||||
* @return re-generated token with the payload in the old one
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String renew(String oldToken, Duration expireAfter) {
|
|
||||||
var resolvedToken = resolve(oldToken);
|
|
||||||
var tokenPayloads = resolvedToken.getPayload();
|
|
||||||
|
|
||||||
var audience = tokenPayloads.getAudience().toArray(new String[]{})[0];
|
|
||||||
var subject = tokenPayloads.getSubject();
|
|
||||||
|
|
||||||
PredefinedKeys.KEYS.forEach(tokenPayloads::remove);
|
|
||||||
|
|
||||||
return createToken(expireAfter, audience, subject, tokenPayloads);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renews the given expired token with the specified custom payload data.
|
|
||||||
*
|
|
||||||
* @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}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String renew(String oldToken, Duration expireAfter, Map<String, Object> payload) {
|
|
||||||
var resolvedTokenClaims = resolve(oldToken).getPayload();
|
|
||||||
var audience = resolvedTokenClaims.getAudience().toArray(new String[]{})[0];
|
|
||||||
var subject = resolvedTokenClaims.getSubject();
|
|
||||||
|
|
||||||
return createToken(expireAfter, audience, subject, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String renew(String oldToken, Map<String, Object> payload) {
|
|
||||||
return renew(oldToken, Duration.ofMinutes(30), payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renews the given expired token with the specified strongly-typed
|
|
||||||
* payload data.
|
|
||||||
*
|
|
||||||
* @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}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T extends TokenPayload> String renew(String oldToken, Duration expireAfter, T payload) {
|
|
||||||
var resolvedTokenClaims = resolve(oldToken).getPayload();
|
|
||||||
var audience = resolvedTokenClaims.getAudience().toArray(new String[]{})[0];
|
|
||||||
var subject = resolvedTokenClaims.getSubject();
|
|
||||||
|
|
||||||
return createToken(expireAfter, audience, subject, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renews the given expired token with the specified strongly-typed
|
|
||||||
* payload data.
|
|
||||||
*
|
|
||||||
* @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}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T extends TokenPayload> String renew(String oldToken, T 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) {
|
|
||||||
var now = LocalDateTime.now();
|
|
||||||
var builder = Jwts.builder()
|
|
||||||
.header().add("typ", "JWT")
|
|
||||||
.and()
|
|
||||||
.issuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
|
|
||||||
.notBefore(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
|
|
||||||
.expiration(Date.from(now.plus(expireAfter).atZone(ZoneId.systemDefault()).toInstant()))
|
|
||||||
.subject(subject)
|
|
||||||
.issuer(this.issuer)
|
|
||||||
.audience().add(audience)
|
|
||||||
.and()
|
|
||||||
.id(jtiCreator.nextId().toString());
|
|
||||||
|
|
||||||
if (claims != null && !claims.isEmpty()) {
|
|
||||||
builder.claims(claims);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.signWith(key, algorithm)
|
|
||||||
.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();
|
|
||||||
}
|
|
||||||
-121
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.simplejwt.jjwt.config;
|
|
||||||
|
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
|
||||||
import com.onixbyte.simplejwt.config.TokenResolverConfig;
|
|
||||||
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
|
||||||
import com.onixbyte.simplejwt.exceptions.UnsupportedAlgorithmException;
|
|
||||||
import com.onixbyte.simplejwt.jjwt.JjwtTokenResolver;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.security.SecureDigestAlgorithm;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@code JjwtTokenResolverConfig} class provides the configuration for
|
|
||||||
* the {@link JjwtTokenResolver}.
|
|
||||||
* <p>
|
|
||||||
* This configuration is used to establish the mapping between the standard
|
|
||||||
* {@link TokenAlgorithm} defined in the
|
|
||||||
* {@code cn.org.codecrafters:simple-jwt-facade} and the specific algorithms
|
|
||||||
* used by the {@code io.jsonwebtoken:jjwt} library, which is the underlying
|
|
||||||
* library used by {@link JjwtTokenResolver} to handle JSON Web Tokens
|
|
||||||
* (JWTs).
|
|
||||||
* <p>
|
|
||||||
* <b>Algorithm Mapping:</b>
|
|
||||||
* The {@code JjwtTokenResolverConfig} allows specifying the relationships
|
|
||||||
* between the standard {@link TokenAlgorithm} instances supported by
|
|
||||||
* {@link JjwtTokenResolver} and the corresponding algorithms used by the
|
|
||||||
* {@code io.jsonwebtoken:jjwt} library. The mapping is achieved using a Map,
|
|
||||||
* where the keys are the standard {@link TokenAlgorithm} instances, and the
|
|
||||||
* values represent the algorithm functions used by
|
|
||||||
* {@code io.jsonwebtoken:jjwt} library for each corresponding key.
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b>
|
|
||||||
* The provided algorithm mapping should be consistent with the actual
|
|
||||||
* algorithms supported and used by the {@code io.jsonwebtoken:jjwt} library.
|
|
||||||
* It is crucial to ensure that the mapping is accurate to enable proper token
|
|
||||||
* validation and processing within the {@link JjwtTokenResolver}.
|
|
||||||
*
|
|
||||||
* @author Zihlu Wang
|
|
||||||
* @version 1.1.1
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public final class JjwtTokenResolverConfig implements TokenResolverConfig<SecureDigestAlgorithm<SecretKey, SecretKey>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the instance for io.jsonwebtoken:jjwt config.
|
|
||||||
* @return the instance for io.jsonwebtoken:jjwt config
|
|
||||||
*/
|
|
||||||
public static JjwtTokenResolverConfig getInstance() {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new JjwtTokenResolverConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the algorithm function corresponding to the specified
|
|
||||||
* {@link TokenAlgorithm}.
|
|
||||||
* <p>
|
|
||||||
* This method returns the algorithm function associated with the given
|
|
||||||
* {@link TokenAlgorithm}. The provided {@link TokenAlgorithm} represents
|
|
||||||
* the specific algorithm for which the corresponding algorithm function is
|
|
||||||
* required. The returned algorithm function represents the function
|
|
||||||
* implementation that can be used by the {@link TokenResolver} to handle
|
|
||||||
* the specific algorithm.
|
|
||||||
*
|
|
||||||
* @param algorithm the {@link TokenAlgorithm} for which the algorithm
|
|
||||||
* function is required
|
|
||||||
* @return the algorithm function associated with the given {@link
|
|
||||||
* TokenAlgorithm}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public SecureDigestAlgorithm<SecretKey, SecretKey> getAlgorithm(TokenAlgorithm algorithm) {
|
|
||||||
if (!SUPPORTED_ALGORITHMS.containsKey(algorithm)) {
|
|
||||||
throw new UnsupportedAlgorithmException("""
|
|
||||||
The request algorithm is not supported by our system yet. Please change to supported ones.""");
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The package {@code cn.org.codecrafters.simplejwt.jjwt.config} contains
|
|
||||||
* configuration classes related to the {@link
|
|
||||||
* com.onixbyte.simplejwt.jjwt.JjwtTokenResolver}
|
|
||||||
* implementation.
|
|
||||||
* <p>
|
|
||||||
* The classes in this package provide configuration options and settings for
|
|
||||||
* the {@link com.onixbyte.simplejwt.jjwt.JjwtTokenResolver},
|
|
||||||
* which is used for resolving JSON Web Tokens (JWT) using the Auth0 library.
|
|
||||||
* <p>
|
|
||||||
* The {@link
|
|
||||||
* com.onixbyte.simplejwt.jjwt.config.JjwtTokenResolverConfig}
|
|
||||||
* class is a configuration class that defines the mapping between standard
|
|
||||||
* {@link com.onixbyte.simplejwt.constants.TokenAlgorithm} and the
|
|
||||||
* corresponding function implementation used by {@link
|
|
||||||
* com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} for handling
|
|
||||||
* JWT algorithms. It enables developers to specify and customize the
|
|
||||||
* algorithm functions according to the chosen JWT algorithm and the library
|
|
||||||
* being used.
|
|
||||||
* <p>
|
|
||||||
* The configuration options in this package help developers integrate and
|
|
||||||
* configure the {@link
|
|
||||||
* com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} seamlessly
|
|
||||||
* into their Spring Boot applications. Developers can fine-tune the token
|
|
||||||
* resolution process and customize algorithm handling to align with their
|
|
||||||
* specific requirements and desired level of security.
|
|
||||||
* <p>
|
|
||||||
* It is recommended to explore the classes in this package to understand how
|
|
||||||
* to configure and use the {@link
|
|
||||||
* com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} effectively
|
|
||||||
* in the Spring Boot environment to handle JWT authentication and
|
|
||||||
* authorisation securely and efficiently.
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
package com.onixbyte.simplejwt.jjwt.config;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This package contains classes related to the integration of the {@code
|
|
||||||
* io.jsonwebtoken:jjwt-api} library in the Simple JWT project. {@code
|
|
||||||
* io.jsonwebtoken:jjwt-api} is a powerful and widely-used Identity as a Service
|
|
||||||
* (IDaaS) platform that provides secure authentication and authorisation
|
|
||||||
* solutions for web and mobile applications. The classes in this package
|
|
||||||
* provide the necessary functionality to handle JSON Web Tokens (JWTs) using
|
|
||||||
* the {@code io.jsonwebtoken:jjwt-api} library.
|
|
||||||
* <p>
|
|
||||||
* The main class in this package is the {@link
|
|
||||||
* com.onixbyte.simplejwt.jjwt.JjwtTokenResolver}, which
|
|
||||||
* implements the {@link com.onixbyte.simplejwt.TokenResolver} interface
|
|
||||||
* and uses the {@code io.jsonwebtoken:jjwt-api} library to handle JWT
|
|
||||||
* operations. It provides the functionality to create, validate, and extract
|
|
||||||
* 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
|
|
||||||
* integrating {@code io.jsonwebtoken:jjwt-api} as the JWT management library.
|
|
||||||
* <p>
|
|
||||||
* The {@link com.onixbyte.simplejwt.jjwt.JjwtTokenResolver} relies on
|
|
||||||
* the {@code io.jsonwebtoken:jjwt-api}
|
|
||||||
* library to handle the underlying JWT operations, including token creation,
|
|
||||||
* validation, and extraction. It utilizes the {@code io.jsonwebtoken:jjwt-api}
|
|
||||||
* {@link io.jsonwebtoken.SignatureAlgorithm} class to define and use different
|
|
||||||
* algorithms for JWT signing and verification.
|
|
||||||
* <p>
|
|
||||||
* To use the {@link com.onixbyte.simplejwt.jjwt.JjwtTokenResolver},
|
|
||||||
* developers must provide the necessary configurations and dependencies, such
|
|
||||||
* as the {@link com.onixbyte.guid.GuidCreator} for generating unique
|
|
||||||
* JWT IDs (JTI), the supported algorithm function, the issuer name, and the
|
|
||||||
* secret key used for token signing and validation. The
|
|
||||||
* {@link com.onixbyte.simplejwt.jjwt.config.JjwtTokenResolverConfig}
|
|
||||||
* class provides a convenient way to configure these dependencies.
|
|
||||||
* <p>
|
|
||||||
* Developers using the {@code io.jsonwebtoken:jjwt-api} integration should be
|
|
||||||
* familiar with the concepts and usage of the {@code io.jsonwebtoken:jjwt-api}
|
|
||||||
* library and follow the official {@code io.jsonwebtoken:jjwt-api}
|
|
||||||
* documentation for best practices and security considerations.
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
package com.onixbyte.simplejwt.jjwt;
|
|
||||||
@@ -25,7 +25,7 @@ val licenseName: String by project
|
|||||||
val licenseUrl: String by project
|
val licenseUrl: String by project
|
||||||
|
|
||||||
val javaJwtVersion: String by project
|
val javaJwtVersion: String by project
|
||||||
val jjwtVersion: String by project
|
val jacksonVersion: String by project
|
||||||
val springBootVersion: String by project
|
val springBootVersion: String by project
|
||||||
|
|
||||||
group = buildGroupId
|
group = buildGroupId
|
||||||
@@ -36,10 +36,7 @@ dependencies {
|
|||||||
implementation(project(":simple-jwt-facade"))
|
implementation(project(":simple-jwt-facade"))
|
||||||
compileOnly("com.auth0:java-jwt:$javaJwtVersion")
|
compileOnly("com.auth0:java-jwt:$javaJwtVersion")
|
||||||
compileOnly(project(":simple-jwt-authzero"))
|
compileOnly(project(":simple-jwt-authzero"))
|
||||||
compileOnly("io.jsonwebtoken:jjwt-api:$jjwtVersion")
|
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
||||||
compileOnly("io.jsonwebtoken:jjwt-impl:$jjwtVersion")
|
|
||||||
compileOnly("io.jsonwebtoken:jjwt-jackson:$jjwtVersion")
|
|
||||||
compileOnly(project(":simple-jwt-jjwt"))
|
|
||||||
implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
|
implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-logging:$springBootVersion")
|
implementation("org.springframework.boot:spring-boot-starter-logging:$springBootVersion")
|
||||||
implementation("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion")
|
implementation("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion")
|
||||||
|
|||||||
+35
-26
@@ -23,6 +23,7 @@ import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
|
|||||||
import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
||||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.onixbyte.simplejwt.constants.TokenAlgorithm;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@@ -35,25 +36,23 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code AuthzeroTokenResolverAutoConfiguration} is responsible for
|
* {@code AuthzeroTokenResolverAutoConfiguration} is responsible for automatically configuring the
|
||||||
* automatically configuring the Simple JWT library with
|
* Simple JWT library with
|
||||||
* {@code com.auth0:java-jwt} when used in a Spring Boot application. It
|
* {@code com.auth0:java-jwt} when used in a Spring Boot application. It provides default settings
|
||||||
* provides default settings and configurations to ensure that the library
|
* and configurations to ensure that the library works smoothly without requiring
|
||||||
* works smoothly without requiring manual configuration.
|
* manual configuration.
|
||||||
* <p>
|
* <p>
|
||||||
* This autoconfiguration class sets up the necessary beans and components
|
* This autoconfiguration class sets up the necessary beans and components required for JWT
|
||||||
* required for JWT generation and validation. It automatically creates and
|
* generation and validation. It automatically creates and configures the
|
||||||
* configures the {@link AuthzeroTokenResolver} bean based on the available
|
* {@link AuthzeroTokenResolver} bean based on the available options and properties.
|
||||||
* options and properties.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Developers using the Simple JWT library with Spring Boot do not need to
|
* Developers using the Simple JWT library with Spring Boot do not need to explicitly configure the
|
||||||
* explicitly configure the library, as the autoconfiguration takes care of
|
* library, as the autoconfiguration takes care of setting up the necessary components and
|
||||||
* setting up the necessary components and configurations automatically.
|
* configurations automatically. However, developers still have the flexibility to customise the
|
||||||
* However, developers still have the flexibility to customise the behavior of
|
* behavior of the library by providing their own configurations and properties.
|
||||||
* the library by providing their own configurations and properties.
|
|
||||||
*
|
*
|
||||||
* @author Zihlu Wang
|
* @author zihluwang
|
||||||
* @version 1.0.0
|
* @version 1.6.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -70,11 +69,13 @@ public class AuthzeroTokenResolverAutoConfiguration {
|
|||||||
* provided SimpleJwtProperties.
|
* provided SimpleJwtProperties.
|
||||||
*
|
*
|
||||||
* @param simpleJwtProperties a {@link SimpleJwtProperties} instance
|
* @param simpleJwtProperties a {@link SimpleJwtProperties} instance
|
||||||
* @param jtiCreator a creator to create ids for JSON Web Token
|
* @param jtiCreator a creator to create ids for JSON Web Token
|
||||||
* @param objectMapper jackson JSON Handler
|
* @param objectMapper jackson JSON Handler
|
||||||
*/
|
*/
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthzeroTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties, @Qualifier("jtiCreator") GuidCreator<?> jtiCreator, ObjectMapper objectMapper) {
|
public AuthzeroTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties,
|
||||||
|
@Qualifier("jtiCreator") GuidCreator<?> jtiCreator,
|
||||||
|
ObjectMapper objectMapper) {
|
||||||
this.jtiCreator = jtiCreator;
|
this.jtiCreator = jtiCreator;
|
||||||
this.simpleJwtProperties = simpleJwtProperties;
|
this.simpleJwtProperties = simpleJwtProperties;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
@@ -90,13 +91,21 @@ public class AuthzeroTokenResolverAutoConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public TokenResolver<DecodedJWT> tokenResolver() {
|
public TokenResolver<DecodedJWT> tokenResolver() {
|
||||||
return new AuthzeroTokenResolver(
|
var builder = AuthzeroTokenResolver.builder();
|
||||||
jtiCreator,
|
|
||||||
simpleJwtProperties.algorithm(),
|
if (TokenAlgorithm.HMAC_ALGORITHMS.contains(simpleJwtProperties.getAlgorithm())) {
|
||||||
simpleJwtProperties.issuer(),
|
builder.keyPair(simpleJwtProperties.getPublicKey(), simpleJwtProperties.getPrivateKey())
|
||||||
simpleJwtProperties.secret(),
|
.algorithm(simpleJwtProperties.getAlgorithm());
|
||||||
objectMapper
|
} else if (TokenAlgorithm.ECDSA_ALGORITHMS.contains(simpleJwtProperties.getAlgorithm())) {
|
||||||
);
|
builder.secret(simpleJwtProperties.getSecret())
|
||||||
|
.algorithm(simpleJwtProperties.getAlgorithm());
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.issuer(simpleJwtProperties.getIssuer());
|
||||||
|
builder.jtiCreator(jtiCreator);
|
||||||
|
builder.objectMapper(objectMapper);
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final GuidCreator<?> jtiCreator;
|
private final GuidCreator<?> jtiCreator;
|
||||||
|
|||||||
-110
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024-2024 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.simplejwt.autoconfiguration;
|
|
||||||
|
|
||||||
import com.onixbyte.guid.GuidCreator;
|
|
||||||
import com.onixbyte.simplejwt.TokenResolver;
|
|
||||||
import com.onixbyte.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
|
||||||
import com.onixbyte.simplejwt.jjwt.JjwtTokenResolver;
|
|
||||||
import io.jsonwebtoken.Claims;
|
|
||||||
import io.jsonwebtoken.Jws;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code JjwtTokenResolverAutoConfiguration} is responsible for automatically
|
|
||||||
* configuring the Simple JWT library with {@code io.jsonwebtoken:jjwt-api}
|
|
||||||
* when used in a Spring Boot application. It provides default settings and
|
|
||||||
* configurations to ensure that the library works smoothly without requiring
|
|
||||||
* manual configuration.
|
|
||||||
* <p>
|
|
||||||
* This autoconfiguration class sets up the necessary beans and components
|
|
||||||
* required for JWT generation and validation. It automatically creates and
|
|
||||||
* configures the {@link JjwtTokenResolver} bean based on the available options
|
|
||||||
* and properties.
|
|
||||||
* <p>
|
|
||||||
* Developers using the Simple JWT library with Spring Boot do not need to
|
|
||||||
* explicitly configure the library, as the autoconfiguration takes care of
|
|
||||||
* setting up the necessary components and configurations automatically.
|
|
||||||
* However, developers still have the flexibility to customize the behavior of
|
|
||||||
* the library by providing their own configurations and properties.
|
|
||||||
*
|
|
||||||
* @author Zihlu Wang
|
|
||||||
* @version 1.1.0
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@AutoConfiguration
|
|
||||||
@EnableConfigurationProperties(value = {SimpleJwtProperties.class})
|
|
||||||
@ConditionalOnClass({Jws.class, Claims.class, JjwtTokenResolver.class})
|
|
||||||
@ConditionalOnMissingBean({TokenResolver.class})
|
|
||||||
@ConditionalOnBean(value = {GuidCreator.class}, name = "jtiCreator")
|
|
||||||
@AutoConfigureAfter(value = GuidAutoConfiguration.class)
|
|
||||||
public class JjwtTokenResolverAutoConfiguration {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new {@code SimpleJwtAutoConfiguration} instance with the provided
|
|
||||||
* {@link SimpleJwtProperties}.
|
|
||||||
*
|
|
||||||
* @param jtiCreator a creator to create JSON Web Token ids
|
|
||||||
* @param simpleJwtProperties the SimpleJwtProperties instance
|
|
||||||
*/
|
|
||||||
@Autowired
|
|
||||||
public JjwtTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties, @Qualifier("jtiCreator") GuidCreator<?> jtiCreator) {
|
|
||||||
this.jtiCreator = jtiCreator;
|
|
||||||
this.simpleJwtProperties = simpleJwtProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link TokenResolver} bean using {@link JjwtTokenResolver} if no existing
|
|
||||||
* {@link TokenResolver} bean is found. The {@link JjwtTokenResolver} is configured with the
|
|
||||||
* provided {@link GuidCreator}, {@code algorithm}, {@code issuer}, and {@code secret}
|
|
||||||
* properties from {@link SimpleJwtProperties}.
|
|
||||||
*
|
|
||||||
* @return the {@link TokenResolver} instance
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public TokenResolver<Jws<Claims>> tokenResolver() {
|
|
||||||
return new JjwtTokenResolver(
|
|
||||||
jtiCreator,
|
|
||||||
simpleJwtProperties.algorithm(),
|
|
||||||
simpleJwtProperties.issuer(),
|
|
||||||
simpleJwtProperties.secret()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
}
|
|
||||||
+6
-25
@@ -20,7 +20,6 @@ package com.onixbyte.simplejwt.autoconfiguration.properties;
|
|||||||
import com.onixbyte.simplejwt.SecretCreator;
|
import com.onixbyte.simplejwt.SecretCreator;
|
||||||
import com.onixbyte.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration;
|
import com.onixbyte.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration;
|
||||||
import com.onixbyte.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;
|
||||||
|
|
||||||
@@ -31,9 +30,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
* "onixbyte.simple-jwt".
|
* "onixbyte.simple-jwt".
|
||||||
* <p>
|
* <p>
|
||||||
* {@code SimpleJwtProperties} provides configuration options for the JWT algorithm, issuer,
|
* {@code SimpleJwtProperties} provides configuration options for the JWT algorithm, issuer,
|
||||||
* and secret. The properties are used by the {@link AuthzeroTokenResolverAutoConfiguration} and
|
* and secret. The properties are used by the {@link AuthzeroTokenResolverAutoConfiguration} to
|
||||||
* {@link JjwtTokenResolver} to set up the necessary configurations for JWT generation
|
* set up the necessary configurations for JWT generation and validation.
|
||||||
* and validation.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Developers can customise the JWT algorithm, issuer, and secret by setting the corresponding
|
* Developers can customise the JWT algorithm, issuer, and secret by setting the corresponding
|
||||||
* properties in the application's properties file. The {@code SimpleJwtAutoConfiguration} class
|
* properties in the application's properties file. The {@code SimpleJwtAutoConfiguration} class
|
||||||
@@ -72,31 +70,14 @@ public class SimpleJwtProperties {
|
|||||||
private String secret = SecretCreator.createSecret(32, true, true, true);
|
private String secret = SecretCreator.createSecret(32, true, true, true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the JWT algorithm configured in the properties.
|
* The private key, PEM formatted.
|
||||||
*
|
|
||||||
* @return the JWT algorithm
|
|
||||||
*/
|
*/
|
||||||
public final TokenAlgorithm algorithm() {
|
private String privateKey;
|
||||||
return algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the issuer value configured in the properties.
|
* The public key, PEM formatted
|
||||||
*
|
|
||||||
* @return the issuer value
|
|
||||||
*/
|
*/
|
||||||
public final String issuer() {
|
private String publicKey;
|
||||||
return issuer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the secret key configured in the properties.
|
|
||||||
*
|
|
||||||
* @return the secret key
|
|
||||||
*/
|
|
||||||
public final String secret() {
|
|
||||||
return secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user