Skip to content

Commit a8ac00b

Browse files
authored
Merge pull request #25 from JavaCoDED78/#24
2 parents 11287ee + 2375e60 commit a8ac00b

File tree

2 files changed

+122
-21
lines changed

2 files changed

+122
-21
lines changed

README.md

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This repository is an open-source Java library for fast and convenient using of
1313
* [How to use](#how-to-use)
1414
* [Instantiate a service](#instantiate-a-service)
1515
* [Persist tokens](#persist-tokens)
16+
* [Invalidate token](#invalidate-jwt-token)
1617
* [Create token](#create-jwt-token)
1718
* [If token is expired](#check-if-jwt-token-is-expired)
1819
* [If token has claim](#check-if-jwt-token-has-claim)
@@ -31,7 +32,7 @@ With Maven add dependency to your `pom.xml`.
3132
<dependency>
3233
<groupId>io.github.javacoded78</groupId>
3334
<artifactId>jwt-humble</artifactId>
34-
<version>0.1.0</version>
35+
<version>0.2.0</version>
3536
</dependency>
3637
```
3738

@@ -55,7 +56,7 @@ After, you can call available methods and use library.
5556

5657
### Persist tokens
5758

58-
Library supports `PersistentTokenServiceImpl` implementation with saving tokens to `TokenStorage`.
59+
Library supports `PersistentTokenService` implementation with saving tokens to `TokenStorage`.
5960

6061
With such approach you can store tokens in Redis or in-memory Map and create new one if no specified tokens exist,
6162
otherwise, stored JWT token would be returned.
@@ -76,18 +77,17 @@ public class Main {
7677

7778
With Redis you need to pass `RedisTokenStorageImpl` object to constructor.
7879

79-
To create `RedisTokenStorageImpl` you need to pass JedisPool
80+
To create `RedisTokenStorageImpl` you need to pass `JedisPool` / host and port / host, port, username and password.
8081

8182
```java
8283
public class Main {
83-
public static void main(String[] args) {
84-
String secret = "aGZiYmtiYWllYmNpZWFpZWJsZWNldWNlY2xhZWNhaWJlbGNhZWN3Q0VCV0VXSUM=";
85-
JedisPoolConfig config = new JedisPoolConfig();
86-
config.setJmxEnabled(false);
87-
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
88-
TokenStorage tokenStorage = new RedisTokenStorageImpl(jedisPool);
89-
TokenService tokenService = new PersistentTokenServiceImpl(secret, tokenStorage);
90-
}
84+
public static void main(String[] args) {
85+
String secret = "aGZiYmtiYWllYmNpZWFpZWJsZWNldWNlY2xhZWNhaWJlbGNhZWN3Q0VCV0VXSUM=";
86+
String host = "localhost";
87+
int port = 6379;
88+
TokenStorage tokenStorage = new RedisTokenStorageImpl(host, port);
89+
PersistentTokenService tokenService = new PersistentTokenServiceImpl(secret, tokenStorage);
90+
}
9191
}
9292
```
9393

@@ -96,6 +96,39 @@ Just pass it as argument in `RedisTokenStorageImpl` constructor.
9696

9797
By default, library uses key `"tokens:" + subject + ":" + type`.
9898

99+
### Invalidate JWT token
100+
101+
With `PersistentTokenService` you can invalidate token by token itself or by subject and token type.
102+
If first option is chosen, all keys with such token values will be deleted.
103+
104+
If token will be deleted from storage you receive `true`.
105+
106+
```java
107+
public class Main {
108+
public static void main(String[] args) {
109+
String token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbmRyb3Nvcjk5QGdtYWlsLmNvbSIsImlkIjozLCJyb2xlcyI6WyJST0xFX1VTRVIiXSwiZXhwIjoxNzE3MTQ1MDIxfQ.w8ZFLFsKf7Qs9_dNb0WzdoyAIpWtfeEyqLfNI_G16_6NHbGwCRbeVVm_a_DzckytsyGYHTWRlZdi_gWK-HjrXg";
110+
boolean deleted = persistentTokenService.invalidate(token);
111+
System.out.println(deleted);
112+
}
113+
}
114+
```
115+
116+
```java
117+
public class Main {
118+
public static void main(String[] args) {
119+
boolean deleted = persistentTokenService.invalidate(
120+
TokenParameters.builder(
121+
122+
"access",
123+
Duration.of(1, ChronoUnit.HOURS)
124+
)
125+
.build()
126+
);
127+
System.out.println(deleted);
128+
}
129+
}
130+
```
131+
99132
### Create JWT token
100133

101134
To create token call method `create(TokenParameters params)` on `TokenService` object.
@@ -104,7 +137,7 @@ To create token call method `create(TokenParameters params)` on `TokenService` o
104137
public class Main {
105138
public static void main(String[] args) {
106139
String token = tokenService.create(
107-
TokenParameters.builder("[email protected]", Duration.of(1, ChronoUnit.HOURS))
140+
TokenParameters.builder("[email protected]", "access", Duration.of(1, ChronoUnit.HOURS))
108141
.build()
109142
);
110143
System.out.println(token);
@@ -135,6 +168,18 @@ class Main {
135168
}
136169
}
137170
```
171+
You can also check expiration with any other date.
172+
173+
```java
174+
class Main {
175+
public static void main(String[] args) {
176+
String token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbmRyb3Nvcjk5QGdtYWlsLmNvbSIsImlkIjozLCJyb2xlcyI6WyJST0xFX1VTRVIiXSwiZXhwIjoxNzE3MTQ1MDIxfQ.w8ZFLFsKf7Qs9_dNb0WzdoyAIpWtfeEyqLfNI_G16_6NHbGwCRbeVVm_a_DzckytsyGYHTWRlZdi_gWK-HjrXg";
177+
Date date = new Date(1705911211182);
178+
boolean expired = tokenService.isExpired(token, date);
179+
System.out.println(expired);
180+
}
181+
}
182+
```
138183

139184
### Check if JWT token has claim
140185

src/main/java/io/github/javacoded78/jwthumble/storage/TokenStorageImpl.java

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,48 @@
11
package io.github.javacoded78.jwthumble.storage;
22

33
import io.github.javacoded78.jwthumble.config.TokenParameters;
4+
import lombok.Getter;
45

5-
import java.util.HashMap;
6+
import java.util.Date;
67
import java.util.Map;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.concurrent.Executors;
10+
import java.util.concurrent.ScheduledExecutorService;
11+
import java.util.concurrent.TimeUnit;
712
import java.util.concurrent.atomic.AtomicBoolean;
813

914
/**
10-
* Basic implementation of TokenStorage. Not thread-safe.
15+
* Basic implementation of TokenStorage.
1116
*/
1217
public class TokenStorageImpl implements TokenStorage {
1318

1419
/**
1520
* Inner map of key-value pairs.
1621
*/
17-
private final Map<String, String> tokens;
22+
private final ConcurrentHashMap<String, TokenEntry> tokens;
23+
24+
/**
25+
* Scheduled executor for cleanup tokens.
26+
*/
27+
private final ScheduledExecutorService scheduler;
1828

1929
/**
2030
* Creates an object.
2131
*/
2232
public TokenStorageImpl() {
23-
this.tokens = new HashMap<>();
33+
this.tokens = new ConcurrentHashMap<>();
34+
this.scheduler = Executors.newSingleThreadScheduledExecutor();
35+
scheduler.scheduleAtFixedRate(
36+
() -> {
37+
Date date = new Date();
38+
tokens.entrySet().removeIf(
39+
entry -> entry.getValue().isAfter(date)
40+
);
41+
},
42+
0,
43+
1,
44+
TimeUnit.SECONDS
45+
);
2446
}
2547

2648
private String subjectTokenKey(final String subject,
@@ -35,7 +57,8 @@ public void save(final String token,
3557
params.getSubject(),
3658
params.getType()
3759
);
38-
tokens.put(tokenKey, token);
60+
TokenEntry entry = new TokenEntry(token, params.getExpiredAt());
61+
tokens.put(tokenKey, entry);
3962
}
4063

4164
@Override
@@ -45,7 +68,11 @@ public boolean exists(final String token,
4568
params.getSubject(),
4669
params.getType()
4770
);
48-
return token.equals(tokens.get(tokenKey));
71+
TokenEntry entry = tokens.get(tokenKey);
72+
if (entry == null) {
73+
return false;
74+
}
75+
return token.equals(entry.token);
4976
}
5077

5178
@Override
@@ -54,14 +81,18 @@ public String get(final TokenParameters params) {
5481
params.getSubject(),
5582
params.getType()
5683
);
57-
return tokens.get(tokenKey);
84+
TokenEntry entry = tokens.get(tokenKey);
85+
if (entry == null) {
86+
return null;
87+
}
88+
return entry.token;
5889
}
5990

6091
@Override
6192
public boolean remove(final String token) {
6293
AtomicBoolean deleted = new AtomicBoolean(false);
63-
for (Map.Entry<String, String> entry : tokens.entrySet()) {
64-
if (entry.getValue().equals(token)) {
94+
for (Map.Entry<String, TokenEntry> entry : tokens.entrySet()) {
95+
if (entry.getValue().token.equals(token)) {
6596
tokens.remove(entry.getKey());
6697
deleted.set(true);
6798
return true;
@@ -79,4 +110,29 @@ public boolean remove(final TokenParameters params) {
79110
return tokens.remove(tokenKey) != null;
80111
}
81112

113+
@Getter
114+
private static class TokenEntry {
115+
116+
/**
117+
* Token.
118+
*/
119+
private final String token;
120+
121+
/**
122+
* Expiration date.
123+
*/
124+
private final Date expiredAt;
125+
126+
TokenEntry(final String token,
127+
final Date expiredAt) {
128+
this.token = token;
129+
this.expiredAt = expiredAt;
130+
}
131+
132+
public boolean isAfter(final Date date) {
133+
return expiredAt.after(date);
134+
}
135+
136+
}
137+
82138
}

0 commit comments

Comments
 (0)