18
18
19
19
import java .nio .charset .StandardCharsets ;
20
20
import java .security .MessageDigest ;
21
- import java .time .Clock ;
22
21
import java .time .Instant ;
22
+ import java .time .temporal .ChronoUnit ;
23
23
import java .util .Base64 ;
24
24
import java .util .Collections ;
25
+ import java .util .LinkedHashMap ;
25
26
import java .util .Map ;
26
- import java .util .concurrent .ConcurrentHashMap ;
27
27
import java .util .function .Function ;
28
28
29
29
import com .nimbusds .jose .JOSEException ;
@@ -146,7 +146,7 @@ private static Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> defaultJwtV
146
146
147
147
private static final class JtiClaimValidator implements OAuth2TokenValidator <Jwt > {
148
148
149
- private static final Map <String , Long > jtiCache = new ConcurrentHashMap <>( );
149
+ private static final Map <String , Long > JTI_CACHE = Collections . synchronizedMap ( new JtiCache () );
150
150
151
151
@ Override
152
152
public OAuth2TokenValidatorResult validate (Jwt jwt ) {
@@ -166,8 +166,8 @@ public OAuth2TokenValidatorResult validate(Jwt jwt) {
166
166
OAuth2Error error = createOAuth2Error ("jti claim is invalid." );
167
167
return OAuth2TokenValidatorResult .failure (error );
168
168
}
169
- Instant now = Instant .now (Clock . systemUTC () );
170
- if ((jtiCache .putIfAbsent (jtiHash , now .toEpochMilli ())) != null ) {
169
+ Instant expiry = Instant .now (). plus ( 1 , ChronoUnit . HOURS );
170
+ if ((JTI_CACHE .putIfAbsent (jtiHash , expiry .toEpochMilli ())) != null ) {
171
171
// Already used
172
172
OAuth2Error error = createOAuth2Error ("jti claim is invalid." );
173
173
return OAuth2TokenValidatorResult .failure (error );
@@ -185,6 +185,21 @@ private static String computeSHA256(String value) throws Exception {
185
185
return Base64 .getUrlEncoder ().withoutPadding ().encodeToString (digest );
186
186
}
187
187
188
+ private static final class JtiCache extends LinkedHashMap <String , Long > {
189
+
190
+ private static final int MAX_SIZE = 1000 ;
191
+
192
+ @ Override
193
+ protected boolean removeEldestEntry (Map .Entry <String , Long > eldest ) {
194
+ if (size () > MAX_SIZE ) {
195
+ return true ;
196
+ }
197
+ Instant expiry = Instant .ofEpochMilli (eldest .getValue ());
198
+ return Instant .now ().isAfter (expiry );
199
+ }
200
+
201
+ }
202
+
188
203
}
189
204
190
205
}
0 commit comments