|
|
|
@ -18,12 +18,12 @@ package org.springframework.security.oauth2.jwt; |
|
|
|
|
|
|
|
|
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.security.MessageDigest; |
|
|
|
import java.security.MessageDigest; |
|
|
|
import java.time.Clock; |
|
|
|
|
|
|
|
import java.time.Instant; |
|
|
|
import java.time.Instant; |
|
|
|
|
|
|
|
import java.time.temporal.ChronoUnit; |
|
|
|
import java.util.Base64; |
|
|
|
import java.util.Base64; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Collections; |
|
|
|
|
|
|
|
import java.util.LinkedHashMap; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
|
|
|
|
import java.util.function.Function; |
|
|
|
import java.util.function.Function; |
|
|
|
|
|
|
|
|
|
|
|
import com.nimbusds.jose.JOSEException; |
|
|
|
import com.nimbusds.jose.JOSEException; |
|
|
|
@ -146,7 +146,7 @@ public final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory<DPoPP |
|
|
|
|
|
|
|
|
|
|
|
private static final class JtiClaimValidator implements OAuth2TokenValidator<Jwt> { |
|
|
|
private static final class JtiClaimValidator implements OAuth2TokenValidator<Jwt> { |
|
|
|
|
|
|
|
|
|
|
|
private static final Map<String, Long> jtiCache = new ConcurrentHashMap<>(); |
|
|
|
private static final Map<String, Long> JTI_CACHE = Collections.synchronizedMap(new JtiCache()); |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public OAuth2TokenValidatorResult validate(Jwt jwt) { |
|
|
|
public OAuth2TokenValidatorResult validate(Jwt jwt) { |
|
|
|
@ -166,8 +166,8 @@ public final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory<DPoPP |
|
|
|
OAuth2Error error = createOAuth2Error("jti claim is invalid."); |
|
|
|
OAuth2Error error = createOAuth2Error("jti claim is invalid."); |
|
|
|
return OAuth2TokenValidatorResult.failure(error); |
|
|
|
return OAuth2TokenValidatorResult.failure(error); |
|
|
|
} |
|
|
|
} |
|
|
|
Instant now = Instant.now(Clock.systemUTC()); |
|
|
|
Instant expiry = Instant.now().plus(1, ChronoUnit.HOURS); |
|
|
|
if ((jtiCache.putIfAbsent(jtiHash, now.toEpochMilli())) != null) { |
|
|
|
if ((JTI_CACHE.putIfAbsent(jtiHash, expiry.toEpochMilli())) != null) { |
|
|
|
// Already used
|
|
|
|
// Already used
|
|
|
|
OAuth2Error error = createOAuth2Error("jti claim is invalid."); |
|
|
|
OAuth2Error error = createOAuth2Error("jti claim is invalid."); |
|
|
|
return OAuth2TokenValidatorResult.failure(error); |
|
|
|
return OAuth2TokenValidatorResult.failure(error); |
|
|
|
@ -185,6 +185,21 @@ public final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory<DPoPP |
|
|
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); |
|
|
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final class JtiCache extends LinkedHashMap<String, Long> { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int MAX_SIZE = 1000; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
protected boolean removeEldestEntry(Map.Entry<String, Long> eldest) { |
|
|
|
|
|
|
|
if (size() > MAX_SIZE) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Instant expiry = Instant.ofEpochMilli(eldest.getValue()); |
|
|
|
|
|
|
|
return Instant.now().isAfter(expiry); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|