Skip to content

Commit c6744e3

Browse files
authored
Java 8 API compatibility changes (#1015)
- Upgraded dependencies and maven plugins to latest versions compatible with JDK 8+. - Remove JDK 7 from CI build - Consolidated `ci.yml` build matrix - JDK 8 through 24 build testing - Removed `io.jsonwebtoken.impl.lang.Function` in favor of `java.util.function.Function` - Marked API interfaces as `@FunctionalInterface` where possible - Migrated `io.jsonwebtoken.lang.Supplier` usages to `java.util.function.Supplier` where possible, except for `RedactedSupplier` usages. - Breaking change: Renamed and moved `io.jsonwebtoken.lang.Supplier` to `io.jsonwebtoken.security.ConfidentialValue` - Breaking change: Renamed `GsonSupplierSerializer` to `GsonConfidentialValueSerializer` - Breaking change: Renamed `JacksonSupplierSerializer` to `JacksonConfidentialValueSerializer` * Declared `@FunctionalInterface` / cleanup where feasible. * Removing explicit Java 7 and Java 8 callouts/references now that 8 is the default/baseline. * Removing explicit Java 7 and Java 8 callouts/references now that 8 is the default/baseline. * Minor JavaDoc enhancement
1 parent 6f6f027 commit c6744e3

File tree

94 files changed

+319
-487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+319
-487
lines changed

.github/workflows/ci.yml

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,20 @@ env:
1111
MVN_CMD: ./mvnw --no-transfer-progress -B
1212

1313
jobs:
14-
oracle:
14+
build:
1515
strategy:
1616
matrix:
17-
java: [ '17' ]
17+
java: [ '8', '11', '17', '21', '24' ]
18+
distribution: [ 'zulu', 'temurin', 'corretto' ]
1819
runs-on: 'ubuntu-latest'
19-
name: jdk-${{ matrix.java }}-oracle
20+
name: jdk-${{ matrix.java }}-${{ matrix.distribution }}
2021
steps:
2122
- uses: actions/checkout@v4
22-
- name: Set up JDK
23-
uses: actions/[email protected]
24-
with:
25-
distribution: oracle
26-
java-version: ${{ matrix.java }}
27-
- name: Install softhsm2
28-
run: sudo apt-get install -y softhsm2
29-
- name: Install opensc
30-
run: sudo apt-get install -y opensc
31-
- name: Ensure SoftHSM user configuration
32-
run: impl/src/test/scripts/softhsm configure
33-
- name: Populate SoftHSM with JJWT test keys
34-
run: impl/src/test/scripts/softhsm import
35-
- name: Build
36-
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
37-
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
38-
run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
39-
40-
temurin:
41-
strategy:
42-
matrix:
43-
java: [ '8', '11', '17', '18' ]
44-
runs-on: 'ubuntu-latest'
45-
name: jdk-${{ matrix.java }}-temurin
46-
steps:
47-
- uses: actions/checkout@v4
48-
- name: Set up JDK
23+
- name: Set up JDK ${{ matrix.java }}-${{ matrix.distribution }}
4924
uses: actions/setup-java@v4
5025
with:
5126
java-version: ${{ matrix.java }}
52-
distribution: 'temurin'
27+
distribution: ${{ matrix.distribution }}
5328
cache: 'maven'
5429
check-latest: true
5530
- name: Install softhsm2
@@ -65,38 +40,6 @@ jobs:
6540
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
6641
run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
6742

68-
zulu:
69-
strategy:
70-
matrix:
71-
java: [ '7', '8', '9', '11', '12', '13', '14', '15', '16', '17', '18', '21' ]
72-
runs-on: 'ubuntu-latest'
73-
env:
74-
JDK_MAJOR_VERSION: ${{ matrix.java }}
75-
name: jdk-${{ matrix.java }}-zulu
76-
steps:
77-
- uses: actions/checkout@v4
78-
- name: Set up JDK
79-
uses: actions/setup-java@v4
80-
with:
81-
java-version: ${{ matrix.java }}
82-
distribution: 'zulu'
83-
cache: 'maven'
84-
check-latest: true
85-
- name: Install softhsm2
86-
run: sudo apt-get install -y softhsm2
87-
- name: Install opensc
88-
run: sudo apt-get install -y opensc
89-
- name: Ensure SoftHSM user configuration
90-
run: impl/src/test/scripts/softhsm configure
91-
- name: Populate SoftHSM with JJWT test keys
92-
run: impl/src/test/scripts/softhsm import
93-
- name: Build
94-
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
95-
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
96-
run: |
97-
if [ "$JDK_MAJOR_VERSION" == "7" ]; then export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=128m"; fi
98-
${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
99-
10043
# ensure all of our files have the correct/updated license header
10144
license-check:
10245
runs-on: 'ubuntu-latest'
@@ -117,9 +60,6 @@ jobs:
11760
${{env.MVN_CMD}} license:check
11861
11962
code-coverage:
120-
# (commented out for now - see the comments in 'Wait to start' below for why. Keeping this here as a placeholder
121-
# as it may be better to use instead of an artificial delay once we no longer need to build on JDK 7):
122-
#needs: zulu # wait until others finish so a coverage failure doesn't cancel others accidentally
12363
runs-on: 'ubuntu-latest'
12464
steps:
12565
- uses: actions/checkout@v4
@@ -139,14 +79,16 @@ jobs:
13979
- name: Populate SoftHSM with JJWT test keys
14080
run: impl/src/test/scripts/softhsm import
14181
- name: Wait to start
142-
# wait a little to start: code coverage usually only takes about 1 1/2 minutes. If coverage fails, it will
143-
# cancel the other running builds, and we don't want that (because we want to see if jobs fail due to
144-
# build issues, not due to the code-coverage job causing the others to cancel). We could have used the
145-
# 'need' property (commented out above), and that would wait until all the other jobs have finished before
146-
# starting this one, but that introduces unnecessary (sometimes 2 1/2 minute) delay, whereas delaying the
147-
# start of the code coverage checks a bit should allow everything to finish around the same time without having
148-
# much of an adverse effect on the other jobs above.
149-
run: sleep 90s
82+
# wait a little to start: SoftHSM install and code coverage usually takes 3 1/2 minutes, and we don't want
83+
# it fail before other jobs; we want to see if jobs fail due to build issues, not just due to the code-coverage
84+
# job causing the others to cancel).
85+
#
86+
# We choose a sleep/delay approach here instead of using the GitHub Actions `needs` attribute
87+
# because `needs` requires its dependency to fully finish before starting this one, and doing so
88+
# would double the overall build time. Instead we want to run this concurrently with the other builds for
89+
# speed. The sleep should allow this to finish around the same time, or just slightly after, all the other
90+
# jobs.
91+
run: sleep 10s
15092
shell: bash
15193
- name: Code Coverage
15294
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
## Release Notes
22

3+
### 0.14.0
4+
5+
The first JJWT release that uses Java 8+ features. This release is not strictly backwards compatible and will also not work with Java 7.
6+
7+
#### Backwards Compatibility Breaking Changes
8+
9+
- The `io.jsonwebtoken.lang.Supplier` interface has been renamed and moved to `io.jsonwebtoken.security.ConfidentialValue` to avoid any potential risk of conflict or accidental use
10+
with `java.util.function.Supplier`. If you have explicitly configured a `Gson` instance to work with this type previously, you must update your usage to reference the new interface, for example:
11+
```java
12+
new GsonBuilder()
13+
.registerTypeHierarchyAdapter(io.jsonwebtoken.security.ConfidentialValue.class, GsonConfidentialValueSerializer.INSTANCE)
14+
// ... etc ...
15+
.create();
16+
```
17+
- The `io.jsonwebtoken.gson.io.GsonSupplierSerializer` class has been renamed to `GsonConfidentialValueSerializer`
18+
- The `io.jsonwebtoken.jackson.io.JacksonSupplierSerializer` has been renamed to `JacksonConfidentialValueSerializer`.`
19+
320
### 0.13.0
421

522
This is the last minor JJWT release branch that will support Java 7. Any necessary emergency bug fixes will be fixed in subsequent `0.13.x` patch releases, but all new development, including Java 8 compatible changes, will be in the next minor (`0.14.0`) release.
@@ -412,7 +429,7 @@ deprecate some concepts, or in some cases, completely break backwards compatibil
412429
`GsonSupplierSerializer` type adapter, for example:
413430
```java
414431
new GsonBuilder()
415-
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
432+
.registerTypeHierarchyAdapter(io.jsonwebtoken.security.ConfidentialValue.class, GsonSupplierSerializer.INSTANCE)
416433
.disableHtmlEscaping().create();
417434
```
418435
This is to ensure JWKs have `toString()` and application log safety (do not print secure material), but still

README.adoc

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ ifdef::env-github[]
1515
endif::[]
1616

1717
// Macros
18-
:fn-require-java8-plus: Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
1918
:fn-require-java11-plus: Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
2019
:fn-require-java15-plus: Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
2120

@@ -56,7 +55,7 @@ toc::[]
5655
5756
== Features
5857
59-
* Fully functional on all Java 7+ JDKs and Android
58+
* Fully functional on all Java 8+ JDKs and Android
6059
* Automatic security best practices and assertions
6160
* Easy to learn and read API
6261
* Convenient and readable http://en.wikipedia.org/wiki/Fluent_interface[fluent] interfaces, great for IDE
@@ -128,16 +127,15 @@ and conditional branch variant in the entire codebase is tested and required to
128127
| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5[AES_256_CBC_HMAC_SHA_512] authenticated encryption algorithm
129128
130129
| `A128GCM`
131-
| AES GCM using 128-bit key^*1*^
130+
| AES GCM using 128-bit key
132131
133132
| `A192GCM`
134-
| AES GCM using 192-bit key^*1*^
133+
| AES GCM using 192-bit key
135134
136135
| `A256GCM`
137-
| AES GCM using 256-bit key^*1*^
136+
| AES GCM using 256-bit key
138137
|===
139138
+
140-
^*1*.{sp}{fn-require-java8-plus}^
141139
142140
* All Key Management Algorithms for obtaining JWE encryption and decryption keys:
143141
+
@@ -178,25 +176,24 @@ and conditional branch variant in the entire codebase is tested and required to
178176
| ECDH-ES using Concat KDF and CEK wrapped with "A256KW"
179177
180178
| `A128GCMKW`
181-
| Key wrapping with AES GCM using 128-bit key^*1*^
179+
| Key wrapping with AES GCM using 128-bit key
182180
183181
| `A192GCMKW`
184-
| Key wrapping with AES GCM using 192-bit key^*1*^
182+
| Key wrapping with AES GCM using 192-bit key
185183
186184
| `A256GCMKW`
187-
| Key wrapping with AES GCM using 256-bit key^*1*^
185+
| Key wrapping with AES GCM using 256-bit key
188186
189187
| `PBES2-HS256+A128KW`
190-
| PBES2 with HMAC SHA-256 and "A128KW" wrapping^*1*^
188+
| PBES2 with HMAC SHA-256 and "A128KW" wrapping
191189
192190
| `PBES2-HS384+A192KW`
193-
| PBES2 with HMAC SHA-384 and "A192KW" wrapping^*1*^
191+
| PBES2 with HMAC SHA-384 and "A192KW" wrapping
194192
195193
| `PBES2‑HS512+A256KW`
196-
| PBES2 with HMAC SHA-512 and "A256KW" wrapping^*1*^
194+
| PBES2 with HMAC SHA-512 and "A256KW" wrapping
197195
|===
198196
+
199-
^*1*.{sp}{fn-require-java8-plus}^
200197
201198
* Creating, parsing and verifying JSON Web Keys (JWKs) in all standard JWA key formats using native Java `Key` types:
202199
+
@@ -2217,19 +2214,17 @@ The JWT specification defines 6 standard Authenticated Encryption algorithms use
22172214

22182215
| `A128GCM`
22192216
| 128
2220-
| AES GCM using 128-bit key^*1*^
2217+
| AES GCM using 128-bit key
22212218

22222219
| `A192GCM`
22232220
| 192
2224-
| AES GCM using 192-bit key^*1*^
2221+
| AES GCM using 192-bit key
22252222

22262223
| `A256GCM`
22272224
| 256
2228-
| AES GCM using 256-bit key^*1*^
2225+
| AES GCM using 256-bit key
22292226
|===
22302227

2231-
^*1*.{sp}{fn-require-java8-plus}^
2232-
22332228
These are all represented as constants in the `io.jsonwebtoken.Jwts.ENC` registry singleton as
22342229
implementations of the `io.jsonwebtoken.security.AeadAlgorithm` interface.
22352230

@@ -2357,26 +2352,24 @@ Content Encryption Key (CEK):
23572352
| ECDH-ES using Concat KDF and CEK wrapped with "A256KW"
23582353

23592354
| `A128GCMKW`
2360-
| Key wrapping with AES GCM using 128-bit key^*1*^
2355+
| Key wrapping with AES GCM using 128-bit key
23612356

23622357
| `A192GCMKW`
2363-
| Key wrapping with AES GCM using 192-bit key^*1*^
2358+
| Key wrapping with AES GCM using 192-bit key
23642359

23652360
| `A256GCMKW`
2366-
| Key wrapping with AES GCM using 256-bit key^*1*^
2361+
| Key wrapping with AES GCM using 256-bit key
23672362

23682363
| `PBES2-HS256+A128KW`
2369-
| PBES2 with HMAC SHA-256 and "A128KW" wrapping^*1*^
2364+
| PBES2 with HMAC SHA-256 and "A128KW" wrapping
23702365

23712366
| `PBES2-HS384+A192KW`
2372-
| PBES2 with HMAC SHA-384 and "A192KW" wrapping^*1*^
2367+
| PBES2 with HMAC SHA-384 and "A192KW" wrapping
23732368

23742369
| `PBES2‑HS512+A256KW`
2375-
| PBES2 with HMAC SHA-512 and "A256KW" wrapping^*1*^
2370+
| PBES2 with HMAC SHA-512 and "A256KW" wrapping
23762371
|===
23772372

2378-
^*1*.{sp}{fn-require-java8-plus}^
2379-
23802373
These are all represented as constants in the `io.jsonwebtoken.Jwts.KEY` registry singleton as
23812374
implementations of the `io.jsonwebtoken.security.KeyAlgorithm` interface.
23822375

@@ -3471,7 +3464,7 @@ If you're curious, JJWT will automatically create an internal default Gson insta
34713464
[,java]
34723465
----
34733466
new GsonBuilder()
3474-
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
3467+
.registerTypeHierarchyAdapter(io.jsonwebtoken.security.ConfidentialValue.class, GsonConfidentialValueSerializer.INSTANCE)
34753468
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
34763469
.setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
34773470
.disableHtmlEscaping()
@@ -3514,7 +3507,7 @@ And then you can specify the `GsonSerializer` using your own `Gson` instance on
35143507
35153508
Gson gson = new GsonBuilder()
35163509
// don't forget this line!:
3517-
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
3510+
.registerTypeHierarchyAdapter(io.jsonwebtoken.security.ConfidentialValue.class, GsonConfidentialValueSerializer.INSTANCE)
35183511
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
35193512
.setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
35203513
.disableHtmlEscaping().create();
@@ -3543,7 +3536,7 @@ Again, as shown above, it is critical to create your `Gson` instance using the `
35433536

35443537
[,java]
35453538
----
3546-
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
3539+
.registerTypeHierarchyAdapter(io.jsonwebtoken.security.ConfidentialValue.class, GsonConfidentialValueSerializer.INSTANCE)
35473540
----
35483541

35493542
to ensure JWK serialization works as expected.

api/src/main/java/io/jsonwebtoken/Clock.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*
2323
* @since 0.7.0
2424
*/
25+
@FunctionalInterface
2526
public interface Clock {
2627

2728
/**

api/src/main/java/io/jsonwebtoken/Identifiable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
*
8282
* @since 0.12.0
8383
*/
84+
@FunctionalInterface
8485
public interface Identifiable {
8586

8687
/**

api/src/main/java/io/jsonwebtoken/Jwts.java

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import io.jsonwebtoken.lang.Builder;
2020
import io.jsonwebtoken.lang.Classes;
2121
import io.jsonwebtoken.lang.Registry;
22-
import io.jsonwebtoken.lang.Supplier;
2322
import io.jsonwebtoken.security.AeadAlgorithm;
2423
import io.jsonwebtoken.security.KeyAlgorithm;
2524
import io.jsonwebtoken.security.KeyPairBuilderSupplier;
@@ -35,6 +34,7 @@
3534
import java.security.PrivateKey;
3635
import java.security.PublicKey;
3736
import java.util.Map;
37+
import java.util.function.Supplier;
3838

3939
/**
4040
* Factory class useful for creating instances of JWT interfaces. Using this factory class can be a good
@@ -121,34 +121,22 @@ private ENC() {
121121

122122
/**
123123
* "AES GCM using 128-bit key" as defined by
124-
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a><b><sup>1</sup></b>. This
124+
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a>. This
125125
* algorithm requires a 128-bit (16 byte) key.
126-
*
127-
* <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime
128-
* classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime
129-
* classpath.</p>
130126
*/
131127
public static final AeadAlgorithm A128GCM = get().forKey("A128GCM");
132128

133129
/**
134130
* &quot;AES GCM using 192-bit key&quot; as defined by
135-
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a><b><sup>1</sup></b>. This
131+
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a>. This
136132
* algorithm requires a 192-bit (24 byte) key.
137-
*
138-
* <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime
139-
* classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime
140-
* classpath.</p>
141133
*/
142134
public static final AeadAlgorithm A192GCM = get().forKey("A192GCM");
143135

144136
/**
145137
* &quot;AES GCM using 256-bit key&quot; as defined by
146-
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a><b><sup>1</sup></b>. This
138+
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a>. This
147139
* algorithm requires a 256-bit (32 byte) key.
148-
*
149-
* <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime
150-
* classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime
151-
* classpath.</p>
152140
*/
153141
public static final AeadAlgorithm A256GCM = get().forKey("A256GCM");
154142
}

api/src/main/java/io/jsonwebtoken/Locator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* @param <T> the type of object that may be returned from the {@link #locate(Header)} method
2929
* @since 0.12.0
3030
*/
31+
@FunctionalInterface
3132
public interface Locator<T> {
3233

3334
/**

api/src/main/java/io/jsonwebtoken/io/Base64Decoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
/**
2121
* Very fast <a href="https://datatracker.ietf.org/doc/html/rfc4648#section-4">Base64</a> decoder guaranteed to
22-
* work in all &gt;= Java 7 JDK and Android environments.
22+
* work in all Java JDK and Android environments.
2323
*
2424
* @since 0.10.0
2525
*/

0 commit comments

Comments
 (0)