Merge branch 'main' into development

# Conflicts:
#	devkit-core/src/main/resources/logback.xml
#	devkit-utils/src/main/resources/logback.xml
#	guid/src/main/resources/logback.xml
#	property-guard-spring-boot-starter/src/main/resources/logback.xml
#	simple-jwt-authzero/src/main/resources/logback.xml
#	simple-jwt-facade/src/main/resources/logback.xml
#	simple-jwt-jjwt/src/main/resources/logback.xml
#	simple-jwt-spring-boot-starter/src/main/resources/logback.xml
#	webcal/src/main/resources/logback.xml
This commit is contained in:
Zihlu Wang
2024-03-31 17:52:47 +08:00
22 changed files with 289 additions and 173 deletions
+8 -24
View File
@@ -6,27 +6,19 @@ JDevKit is a Java Development Kit that offers a set of convenient tools for writ
> For more information, please visit the README file of each module. > For more information, please visit the README file of each module.
### `devkit-core` ### `devkit-core` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/devkit-core/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/devkit-core/README.md)_</span>
The core module for `JDevKit`, by now, this module contains the commonly used classes of the whole `dev-kit`. The core module for `JDevKit`, by now, this module contains the commonly used classes of the whole `dev-kit`.
### `devkit-utils` ### `devkit-utils` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/devkit-utils/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/devkit-utils/README.md)_</span>
A collection of common utility classes to simplify Java development. It includes tools for Base64 encoding/decoding of strings, reducing if-else code blocks using Lambda expressions, converting between maps and arbitrary objects, high-precision chained mathematical calculations, and string hashing or message digest calculations. A collection of common utility classes to simplify Java development. It includes tools for Base64 encoding/decoding of strings, reducing if-else code blocks using Lambda expressions, converting between maps and arbitrary objects, high-precision chained mathematical calculations, and string hashing or message digest calculations.
### `guid` ### `guid` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/guid/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/guid/README.md)_</span>
A module for generating globally unique IDs. It includes a facade interface and an implementation of GUID generation using the Snowflake algorithm. More globally unique ID generation modes will be added in the future. A module for generating globally unique IDs. It includes a facade interface and an implementation of GUID generation using the Snowflake algorithm. More globally unique ID generation modes will be added in the future.
### `WebCal` ### `WebCal` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/webcal/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/webcal/README.md)_</span>
The module `webcal` is a Java library that facilitates the generation and resolution of iCalendar content for web-based calendar applications. It provides a flexible and easy-to-use API for creating web calendars with customisable settings and events. The module `webcal` is a Java library that facilitates the generation and resolution of iCalendar content for web-based calendar applications. It provides a flexible and easy-to-use API for creating web calendars with customisable settings and events.
@@ -34,27 +26,19 @@ With the `webcal` module, developers can easily integrate calendar functionality
Please note that the `webcal` module adheres to the iCalendar standard specified in RFC 5545, ensuring compatibility with other calendar applications that support this format. Please note that the `webcal` module adheres to the iCalendar standard specified in RFC 5545, ensuring compatibility with other calendar applications that support this format.
### `simple-jwt-facade` ### `simple-jwt-facade` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-facade/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-facade/README.md)_</span>
A facade for Simple JWT (JSON Web Token) implementations in Java. This module provides a unified interface to work with JWTs regardless of the underlying implementation. A facade for Simple JWT (JSON Web Token) implementations in Java. This module provides a unified interface to work with JWTs regardless of the underlying implementation.
### `simple-jwt-authzero` ### `simple-jwt-authzero` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-authzero/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-authzero/README.md)_</span>
A Simple JWT implementation using the com.auth0:java-jwt library. A Simple JWT implementation using the com.auth0:java-jwt library.
### `simple-jwt-jjwt` ### `simple-jwt-jjwt` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-jjwt/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-jjwt/README.md)_</span>
A Simple JWT implementation using the `io.jsonwebtoken:jjwt-api` library. A Simple JWT implementation using the `io.jsonwebtoken:jjwt-api` library.
### `simple-jwt-spring-boot-starter` ### `simple-jwt-spring-boot-starter` <span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-spring-boot-starter/README.md)_</span>
<span style="font-size: 14px;">_[Learn more](https://github.com/CodeCraftersCN/jdevkit/simple-jwt-spring-boot-starter/README.md)_</span>
A Spring Boot autoconfiguration wrapper for the simple-jwt module, making it easier to integrate JWT functionality into Spring Boot applications. A Spring Boot autoconfiguration wrapper for the simple-jwt module, making it easier to integrate JWT functionality into Spring Boot applications.
+23 -6
View File
@@ -1,14 +1,31 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project 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
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
java { java {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
@@ -24,9 +41,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("devkitCore") { create<MavenPublication>("devkitCore") {
groupId = sGroupId groupId = buildGroupId
artifactId = "devkit-core" artifactId = "devkit-core"
version = sVersion version = buildVersion
pom { pom {
name = "DevKit - Core" name = "DevKit - Core"
+2 -4
View File
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+23 -6
View File
@@ -1,14 +1,31 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project 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
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-core")) implementation(project(":devkit-core"))
@@ -28,9 +45,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("devkitUtils") { create<MavenPublication>("devkitUtils") {
groupId = sGroupId groupId = buildGroupId
artifactId = "devkit-utils" artifactId = "devkit-utils"
version = sVersion version = buildVersion
pom { pom {
name = "DevKit - Utils" name = "DevKit - Utils"
+2 -4
View File
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+5 -5
View File
@@ -21,13 +21,13 @@ slf4jVersion=2.0.9
lombokVersion=1.18.30 lombokVersion=1.18.30
jacksonVersion=2.16.0 jacksonVersion=2.16.0
javaJwtVersion=4.4.0 javaJwtVersion=4.4.0
jjwtVersion=0.11.5 jjwtVersion=0.12.5
okhttpVersion=4.12.0 okhttpVersion=4.12.0
springVersion=6.1.1 springVersion=6.1.3
springBootVersion=3.2.0 springBootVersion=3.2.3
sGroupId=cn.org.codecrafters buildGroupId=cn.org.codecrafters
sVersion=1.3.2 buildVersion=1.4.0
projectUrl=https://codecrafters.org.cn/JDevKit projectUrl=https://codecrafters.org.cn/JDevKit
projectGithubUrl=https://github.com/CodeCraftersCN/JDevKit projectGithubUrl=https://github.com/CodeCraftersCN/JDevKit
licenseName=The Apache License, Version 2.0 licenseName=The Apache License, Version 2.0
+23 -6
View File
@@ -1,14 +1,31 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project 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
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-core")) implementation(project(":devkit-core"))
@@ -28,9 +45,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("guid") { create<MavenPublication>("guid") {
groupId = sGroupId groupId = buildGroupId
artifactId = "guid" artifactId = "guid"
version = sVersion version = buildVersion
pom { pom {
name = "DevKit - GUID" name = "DevKit - GUID"
+2 -5
View File
@@ -15,12 +15,9 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
@@ -1,7 +1,24 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project val projectGithubUrl: String by project
val licenseName: String by project val licenseName: String by project
@@ -9,8 +26,8 @@ val licenseUrl: String by project
val springBootVersion: String by project val springBootVersion: String by project
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-utils")) implementation(project(":devkit-utils"))
@@ -34,9 +51,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("propertyGuardSpringBootStarter") { create<MavenPublication>("propertyGuardSpringBootStarter") {
groupId = sGroupId groupId = buildGroupId
artifactId = "property-guard-spring-boot-starter" artifactId = "property-guard-spring-boot-starter"
version = sVersion version = buildVersion
pom { pom {
name = "Property Guard Spring Boot Starter" name = "Property Guard Spring Boot Starter"
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+23 -6
View File
@@ -1,7 +1,24 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project val projectGithubUrl: String by project
val licenseName: String by project val licenseName: String by project
@@ -10,8 +27,8 @@ val licenseUrl: String by project
val jacksonVersion: String by project val jacksonVersion: String by project
val javaJwtVersion: String by project val javaJwtVersion: String by project
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-utils")) implementation(project(":devkit-utils"))
@@ -35,9 +52,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("simpleJwtAuthzero") { create<MavenPublication>("simpleJwtAuthzero") {
groupId = sGroupId groupId = buildGroupId
artifactId = "simple-jwt-authzero" artifactId = "simple-jwt-authzero"
version = sVersion version = buildVersion
pom { pom {
name = "Simple JWT :: Auth0" name = "Simple JWT :: Auth0"
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+23 -6
View File
@@ -1,14 +1,31 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project 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
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-core")) implementation(project(":devkit-core"))
@@ -30,9 +47,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("simpleJwtFacade") { create<MavenPublication>("simpleJwtFacade") {
groupId = sGroupId groupId = buildGroupId
artifactId = "simple-jwt-facade" artifactId = "simple-jwt-facade"
version = sVersion version = buildVersion
pom { pom {
name = "Simple JWT :: Facade" name = "Simple JWT :: Facade"
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+23 -6
View File
@@ -1,7 +1,24 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project val projectGithubUrl: String by project
val licenseName: String by project val licenseName: String by project
@@ -10,8 +27,8 @@ val licenseUrl: String by project
val jacksonVersion: String by project val jacksonVersion: String by project
val jjwtVersion: String by project val jjwtVersion: String by project
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-utils")) implementation(project(":devkit-utils"))
@@ -37,9 +54,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("simpleJwtJjwt") { create<MavenPublication>("simpleJwtJjwt") {
groupId = sGroupId groupId = buildGroupId
artifactId = "simple-jwt-jjwt" artifactId = "simple-jwt-jjwt"
version = sVersion version = buildVersion
pom { pom {
name = "Simple JWT :: JJWT" name = "Simple JWT :: JJWT"
@@ -28,17 +28,16 @@ import cn.org.codecrafters.simplejwt.constants.PredefinedKeys;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException; import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException;
import cn.org.codecrafters.simplejwt.jjwt.config.JjwtTokenResolverConfig; import cn.org.codecrafters.simplejwt.jjwt.config.JjwtTokenResolverConfig;
import com.fasterxml.jackson.core.type.TypeReference;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import javax.crypto.SecretKey;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
@@ -46,7 +45,7 @@ import java.util.*;
/** /**
* The {@link JjwtTokenResolver} class is an implementation of the {@link * The {@link JjwtTokenResolver} class is an implementation of the {@link
* cn.org.codecrafters.simplejwt.TokenResolver} interface. It uses the {@code * TokenResolver} interface. It uses the {@code
* io.jsonwebtoken:jjwt} library to handle JSON Web Token (JWT) resolution. * io.jsonwebtoken:jjwt} library to handle JSON Web Token (JWT) resolution.
* This resolver provides functionality to create, extract, verify, and renew * This resolver provides functionality to create, extract, verify, and renew
* JWT tokens using various algorithms and custom payload data. * JWT tokens using various algorithms and custom payload data.
@@ -92,7 +91,7 @@ import java.util.*;
* @see Claims * @see Claims
* @see Jws * @see Jws
* @see Jwts * @see Jwts
* @see SignatureAlgorithm * @see SecureDigestAlgorithm
* @see Keys * @see Keys
* @since 1.0.0 * @since 1.0.0
*/ */
@@ -101,14 +100,22 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
private final GuidCreator<?> jtiCreator; private final GuidCreator<?> jtiCreator;
private final SignatureAlgorithm algorithm; private final SecureDigestAlgorithm<SecretKey, SecretKey> algorithm;
private final String issuer; private final String issuer;
private final Key key; private final SecretKey key;
private final JjwtTokenResolverConfig config = JjwtTokenResolverConfig.getInstance(); private final JjwtTokenResolverConfig config = JjwtTokenResolverConfig.getInstance();
/**
* 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) { public JjwtTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, String issuer, String secret) {
if (Objects.isNull(secret) || secret.isBlank()) { if (Objects.isNull(secret) || secret.isBlank()) {
throw new IllegalArgumentException("A secret is required to build a JSON Web Token."); throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
@@ -129,6 +136,13 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); 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) { public JjwtTokenResolver(TokenAlgorithm algorithm, String issuer, String secret) {
if (secret == null || secret.isBlank()) { if (secret == null || secret.isBlank()) {
throw new IllegalArgumentException("A secret is required to build a JSON Web Token."); throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
@@ -149,6 +163,13 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); 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) { public JjwtTokenResolver(String issuer, String secret) {
if (secret == null || secret.isBlank()) { if (secret == null || secret.isBlank()) {
throw new IllegalArgumentException("A secret is required to build a JSON Web Token."); throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
@@ -169,6 +190,12 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); 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) { public JjwtTokenResolver(String issuer) {
this.jtiCreator = UUID::randomUUID; this.jtiCreator = UUID::randomUUID;
this.algorithm = config.getAlgorithm(TokenAlgorithm.HS256); this.algorithm = config.getAlgorithm(TokenAlgorithm.HS256);
@@ -176,26 +203,6 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
this.key = Keys.hmacShaKeyFor(SecretCreator.createSecret(32, true, true, true).getBytes(StandardCharsets.UTF_8)); this.key = Keys.hmacShaKeyFor(SecretCreator.createSecret(32, true, true, true).getBytes(StandardCharsets.UTF_8));
} }
private String buildToken(Duration expireAfter, String audience, String subject, Map<String, Object> claims) {
var now = LocalDateTime.now();
var builder = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
.setNotBefore(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
.setExpiration(Date.from(now.plus(expireAfter).atZone(ZoneId.systemDefault()).toInstant()))
.setSubject(subject)
.setAudience(audience)
.setIssuer(this.issuer)
.setId(jtiCreator.nextId().toString());
if (claims != null && !claims.isEmpty()) {
builder.addClaims(claims);
}
return builder.signWith(key, algorithm)
.compact();
}
/** /**
* Creates a new token with the specified expiration time, subject, and * Creates a new token with the specified expiration time, subject, and
* audience. * audience.
@@ -280,10 +287,10 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
*/ */
@Override @Override
public Jws<Claims> resolve(String token) { public Jws<Claims> resolve(String token) {
return Jwts.parserBuilder() return Jwts.parser()
.setSigningKey(key) .verifyWith(key)
.build() .build()
.parseClaimsJws(token); .parseSignedClaims(token);
} }
/** /**
@@ -300,7 +307,7 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
public <T extends TokenPayload> T extract(String token, Class<T> targetType) { public <T extends TokenPayload> T extract(String token, Class<T> targetType) {
var resolvedToken = resolve(token); var resolvedToken = resolve(token);
var claims = resolvedToken.getBody(); var claims = resolvedToken.getPayload();
try { try {
var bean = targetType.getConstructor().newInstance(); var bean = targetType.getConstructor().newInstance();
@@ -351,9 +358,9 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
@Override @Override
public String renew(String oldToken, Duration expireAfter) { public String renew(String oldToken, Duration expireAfter) {
var resolvedToken = resolve(oldToken); var resolvedToken = resolve(oldToken);
var tokenPayloads = resolvedToken.getBody(); var tokenPayloads = resolvedToken.getPayload();
var audience = tokenPayloads.getAudience(); var audience = tokenPayloads.getAudience().toArray(new String[]{})[0];
var subject = tokenPayloads.getSubject(); var subject = tokenPayloads.getSubject();
PredefinedKeys.KEYS.forEach(tokenPayloads::remove); PredefinedKeys.KEYS.forEach(tokenPayloads::remove);
@@ -372,8 +379,8 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
*/ */
@Override @Override
public String renew(String oldToken, Duration expireAfter, Map<String, Object> payload) { public String renew(String oldToken, Duration expireAfter, Map<String, Object> payload) {
var resolvedTokenClaims = resolve(oldToken).getBody(); var resolvedTokenClaims = resolve(oldToken).getPayload();
var audience = resolvedTokenClaims.getAudience(); var audience = resolvedTokenClaims.getAudience().toArray(new String[]{})[0];
var subject = resolvedTokenClaims.getSubject(); var subject = resolvedTokenClaims.getSubject();
return createToken(expireAfter, audience, subject, payload); return createToken(expireAfter, audience, subject, payload);
@@ -404,8 +411,8 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
*/ */
@Override @Override
public <T extends TokenPayload> String renew(String oldToken, Duration expireAfter, T payload) { public <T extends TokenPayload> String renew(String oldToken, Duration expireAfter, T payload) {
var resolvedTokenClaims = resolve(oldToken).getBody(); var resolvedTokenClaims = resolve(oldToken).getPayload();
var audience = resolvedTokenClaims.getAudience(); var audience = resolvedTokenClaims.getAudience().toArray(new String[]{})[0];
var subject = resolvedTokenClaims.getSubject(); var subject = resolvedTokenClaims.getSubject();
return createToken(expireAfter, audience, subject, payload); return createToken(expireAfter, audience, subject, payload);
@@ -424,4 +431,26 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
public <T extends TokenPayload> String renew(String oldToken, T payload) { public <T extends TokenPayload> String renew(String oldToken, T payload) {
return renew(oldToken, Duration.ofMinutes(30), payload); return renew(oldToken, Duration.ofMinutes(30), payload);
} }
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();
}
} }
@@ -22,8 +22,12 @@ import cn.org.codecrafters.simplejwt.config.TokenResolverConfig;
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm; import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
import cn.org.codecrafters.simplejwt.exceptions.UnsupportedAlgorithmException; import cn.org.codecrafters.simplejwt.exceptions.UnsupportedAlgorithmException;
import cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver; import cn.org.codecrafters.simplejwt.jjwt.JjwtTokenResolver;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.MacAlgorithm;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import javax.crypto.SecretKey;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -57,15 +61,15 @@ import java.util.Map;
* @version 1.1.1 * @version 1.1.1
* @since 1.0.0 * @since 1.0.0
*/ */
public final class JjwtTokenResolverConfig implements TokenResolverConfig<SignatureAlgorithm> { public final class JjwtTokenResolverConfig implements TokenResolverConfig<SecureDigestAlgorithm<SecretKey, SecretKey>> {
private JjwtTokenResolverConfig() { private JjwtTokenResolverConfig() {
} }
private static final Map<TokenAlgorithm, SignatureAlgorithm> SUPPORTED_ALGORITHMS = new HashMap<>() {{ private static final Map<TokenAlgorithm, SecureDigestAlgorithm<SecretKey, SecretKey>> SUPPORTED_ALGORITHMS = new HashMap<>() {{
put(TokenAlgorithm.HS256, SignatureAlgorithm.HS256); put(TokenAlgorithm.HS256, Jwts.SIG.HS256);
put(TokenAlgorithm.HS384, SignatureAlgorithm.HS384); put(TokenAlgorithm.HS384, Jwts.SIG.HS384);
put(TokenAlgorithm.HS512, SignatureAlgorithm.HS512); put(TokenAlgorithm.HS512, Jwts.SIG.HS512);
}}; }};
private static JjwtTokenResolverConfig instance; private static JjwtTokenResolverConfig instance;
@@ -95,7 +99,7 @@ public final class JjwtTokenResolverConfig implements TokenResolverConfig<Signat
* TokenAlgorithm} * TokenAlgorithm}
*/ */
@Override @Override
public SignatureAlgorithm getAlgorithm(TokenAlgorithm algorithm) { public SecureDigestAlgorithm<SecretKey, SecretKey> getAlgorithm(TokenAlgorithm algorithm) {
if (!SUPPORTED_ALGORITHMS.containsKey(algorithm)) { if (!SUPPORTED_ALGORITHMS.containsKey(algorithm)) {
throw new UnsupportedAlgorithmException(""" throw new UnsupportedAlgorithmException("""
The request algorithm is not supported by our system yet. Please change to supported ones."""); The request algorithm is not supported by our system yet. Please change to supported ones.""");
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
@@ -17,8 +17,8 @@
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project val projectGithubUrl: String by project
val licenseName: String by project val licenseName: String by project
@@ -28,8 +28,8 @@ val javaJwtVersion: String by project
val jjwtVersion: String by project val jjwtVersion: String by project
val springBootVersion: String by project val springBootVersion: String by project
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":guid")) implementation(project(":guid"))
@@ -60,9 +60,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("simpleJwtSpringBootStarter") { create<MavenPublication>("simpleJwtSpringBootStarter") {
groupId = sGroupId groupId = buildGroupId
artifactId = "simple-jwt-spring-boot-starter" artifactId = "simple-jwt-spring-boot-starter"
version = sVersion version = buildVersion
pom { pom {
name = "Simple JWT :: Spring Boot Starter" name = "Simple JWT :: Spring Boot Starter"
@@ -17,10 +17,8 @@
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+6 -6
View File
@@ -1,14 +1,14 @@
import java.net.URI import java.net.URI
val sGroupId: String by project val buildGroupId: String by project
val sVersion: String by project val buildVersion: String by project
val projectUrl: String by project val projectUrl: String by project
val projectGithubUrl: String by project 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
group = sGroupId group = buildGroupId
version = sVersion version = buildVersion
dependencies { dependencies {
implementation(project(":devkit-core")) implementation(project(":devkit-core"))
@@ -28,9 +28,9 @@ tasks.test {
publishing { publishing {
publications { publications {
create<MavenPublication>("webcal") { create<MavenPublication>("webcal") {
groupId = sGroupId groupId = buildGroupId
artifactId = "webcal" artifactId = "webcal"
version = sVersion version = buildVersion
pom { pom {
name = "DevKit :: WebCal" name = "DevKit :: WebCal"
+2 -5
View File
@@ -15,12 +15,9 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<configuration> <configuration>
<property name="COLOURFUL_OUTPUT" <property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/> <property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">