Skip to content

Commit 94b18b8

Browse files
committed
Created JWT standalone java demo
1 parent b7f7804 commit 94b18b8

File tree

8 files changed

+197
-2
lines changed

8 files changed

+197
-2
lines changed

build.gradle

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ project(':rxjava-demo') {}
2424
project(':elastic-demo') {}
2525
project(':rabbitmq-demo') {}
2626
project(':google-apis') {}
27-
project(':jce-demo') {}
2827
project(':jackson-fasterxml-demo') {}
2928
project(':cached-model-demo') {}
29+
30+
project(':jce-demo') {}
31+
project(':jwt-demo') {
32+
dependencies {
33+
implementation project(":jce-demo");
34+
}
35+
}

jwt-demo/.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.gradle
2+
.settings
3+
bin
4+
out
5+
build
6+
build_gradle
7+
target
8+
.classpath
9+
.project
10+
.idea
11+
*.iml
12+

jwt-demo/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# JWT -demo
2+
3+
Issue and validate JWT using [nimbus-jose-jwt](https://connect2id.com/products/nimbus-jose-jwt) java library.

jwt-demo/build.gradle

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
apply plugin: 'java'
3+
apply plugin: 'maven'
4+
apply plugin: 'maven-publish'
5+
6+
sourceCompatibility = 11
7+
targetCompatibility = 11
8+
9+
repositories {
10+
mavenCentral()
11+
mavenLocal()
12+
}
13+
14+
dependencies {
15+
implementation 'org.slf4j:slf4j-api:1.7.30'
16+
implementation 'org.slf4j:slf4j-simple:1.7.30'
17+
implementation 'org.bouncycastle:bcpg-jdk15on:1.65'
18+
implementation 'org.bouncycastle:bcpkix-jdk15on:1.65'
19+
implementation 'com.nimbusds:nimbus-jose-jwt:8.9'
20+
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
21+
}
22+
23+
test {
24+
useJUnitPlatform()
25+
testLogging {
26+
events "passed", "skipped", "failed"
27+
}
28+
}
29+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package itx.examples.jwt;
2+
3+
import com.nimbusds.jose.JOSEException;
4+
import com.nimbusds.jose.JOSEObjectType;
5+
import com.nimbusds.jose.JWSAlgorithm;
6+
import com.nimbusds.jose.JWSHeader;
7+
import com.nimbusds.jose.JWSObject;
8+
import com.nimbusds.jose.Payload;
9+
import com.nimbusds.jose.crypto.RSASSASigner;
10+
import com.nimbusds.jose.crypto.RSASSAVerifier;
11+
import com.nimbusds.jwt.SignedJWT;
12+
import net.minidev.json.JSONObject;
13+
14+
import java.security.PrivateKey;
15+
import java.security.cert.X509Certificate;
16+
import java.security.interfaces.RSAPublicKey;
17+
import java.text.ParseException;
18+
import java.util.Date;
19+
20+
public class JWTUtils {
21+
22+
private JWTUtils() {
23+
}
24+
25+
public static JWToken issue(String subject, String keyId, PrivateKey privateKey, Long expires) throws JOSEException {
26+
27+
JSONObject payload = new JSONObject();
28+
JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, JOSEObjectType.JWT, null, null, null, null, null, null, null, null, keyId, null, null);
29+
payload.put("sub", subject);
30+
payload.put("exp", expires);
31+
JWSObject jwsObject = new JWSObject(header, new Payload(payload));
32+
jwsObject.sign(new RSASSASigner(privateKey));
33+
return new JWToken(jwsObject.serialize());
34+
}
35+
36+
public static boolean validate(JWToken jwToken, String subject, String keyId, X509Certificate certificate) throws ParseException, JOSEException {
37+
RSASSAVerifier verifier = new RSASSAVerifier((RSAPublicKey)certificate.getPublicKey());
38+
SignedJWT signedJWT = SignedJWT.parse(jwToken.getToken());
39+
boolean verified = signedJWT.verify(verifier);
40+
String sub = signedJWT.getJWTClaimsSet().getSubject();
41+
String kid = signedJWT.getHeader().getKeyID();
42+
Date expires = signedJWT.getJWTClaimsSet().getExpirationTime();
43+
Date nowDate = new Date();
44+
boolean expired = nowDate.getTime() > expires.getTime();
45+
return verified && subject.equals(sub) && keyId.equals(kid) && !expired;
46+
}
47+
48+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package itx.examples.jwt;
2+
3+
public class JWToken {
4+
5+
private final String token;
6+
7+
public JWToken(String token) {
8+
this.token = token;
9+
}
10+
11+
public String getToken() {
12+
return token;
13+
}
14+
15+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package itx.examples.jwt.tests;
2+
3+
import com.nimbusds.jose.JOSEException;
4+
import itx.examples.jce.JCEUtils;
5+
import itx.examples.jce.KeyPairHolder;
6+
import itx.examples.jce.PKIException;
7+
import itx.examples.jwt.JWTUtils;
8+
import itx.examples.jwt.JWToken;
9+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
10+
import org.junit.jupiter.api.BeforeAll;
11+
import org.junit.jupiter.api.MethodOrderer;
12+
import org.junit.jupiter.api.Order;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.TestMethodOrder;
15+
16+
import java.security.Security;
17+
import java.text.ParseException;
18+
import java.time.Instant;
19+
import java.util.Date;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.concurrent.TimeUnit;
23+
24+
import static org.junit.jupiter.api.Assertions.assertFalse;
25+
import static org.junit.jupiter.api.Assertions.assertNotNull;
26+
import static org.junit.jupiter.api.Assertions.assertTrue;
27+
28+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
29+
public class JWTTests {
30+
31+
private static Map<String, KeyPairHolder> keyCache;
32+
private static JWToken validJWToken;
33+
34+
@BeforeAll
35+
public static void init() throws PKIException {
36+
Security.addProvider(new BouncyCastleProvider());
37+
keyCache = new HashMap<>();
38+
keyCache.put("key-001", JCEUtils.generateSelfSignedKeyPairHolder("issuer", new Date(), 1L, TimeUnit.HOURS));
39+
keyCache.put("key-002", JCEUtils.generateSelfSignedKeyPairHolder("issuer", new Date(), 1L, TimeUnit.HOURS));
40+
}
41+
42+
@Test
43+
@Order(1)
44+
public void issueValidToken() throws JOSEException {
45+
KeyPairHolder keyPairHolder = keyCache.get("key-001");
46+
Long expires = Instant.now().getEpochSecond() + 3600L;
47+
validJWToken = JWTUtils.issue("user", "key-001", keyPairHolder.getPrivateKey(), expires);
48+
assertNotNull(validJWToken);
49+
assertNotNull(validJWToken.getToken());
50+
}
51+
52+
@Test
53+
@Order(2)
54+
public void validateValidToken() throws ParseException, JOSEException {
55+
KeyPairHolder keyPairHolder = keyCache.get("key-001");
56+
boolean result = JWTUtils.validate(validJWToken, "user", "key-001", keyPairHolder.getCertificate());
57+
assertTrue(result);
58+
}
59+
60+
@Test
61+
@Order(3)
62+
public void validateValidTokenUsingIncorrectCertificate() throws ParseException, JOSEException {
63+
KeyPairHolder keyPairHolder = keyCache.get("key-002");
64+
boolean result = JWTUtils.validate(validJWToken, "user", "key-002", keyPairHolder.getCertificate());
65+
assertFalse(result);
66+
}
67+
68+
@Test
69+
@Order(4)
70+
public void issueAndTestExpiredToken() throws ParseException, JOSEException {
71+
KeyPairHolder keyPairHolder = keyCache.get("key-001");
72+
Long expires = Instant.now().getEpochSecond() - 3600L;
73+
validJWToken = JWTUtils.issue("user", "key-001", keyPairHolder.getPrivateKey(), expires);
74+
assertNotNull(validJWToken);
75+
assertNotNull(validJWToken.getToken());
76+
boolean result = JWTUtils.validate(validJWToken, "user", "key-001", keyPairHolder.getCertificate());
77+
assertFalse(result);
78+
}
79+
80+
}

settings.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,6 @@ project(":rmq-data-client").projectDir = new File("rabbitmq-demo/rmq-data-client
6262
include "google-apis"
6363
include "jce-demo"
6464
include "jackson-fasterxml-demo"
65-
include "cached-model-demo"
65+
include "cached-model-demo"
66+
67+
include "jwt-demo"

0 commit comments

Comments
 (0)