From 3d571e3804cabae4373f206e9ea25b8b9dbf3f13 Mon Sep 17 00:00:00 2001 From: Fai Luk Date: Sun, 15 Mar 2026 22:37:21 +0800 Subject: [PATCH] update the jwt related expired log and expiry time --- src/main/java/com/ffii/core/utils/JwtTokenUtil.java | 11 ++++++++++- .../fpsms/config/security/jwt/JwtRequestFilter.java | 3 ++- src/main/resources/application-prod.yml | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/ffii/core/utils/JwtTokenUtil.java b/src/main/java/com/ffii/core/utils/JwtTokenUtil.java index a502738..a75cf96 100644 --- a/src/main/java/com/ffii/core/utils/JwtTokenUtil.java +++ b/src/main/java/com/ffii/core/utils/JwtTokenUtil.java @@ -40,6 +40,10 @@ public class JwtTokenUtil implements Serializable { @Value("${jwt.refresh-expiration-days:30}") private int refreshExpirationDays = 30; + /** Allow up to this many seconds of clock skew so tokens that just expired still parse (reduces boundary failures). */ + @Value("${jwt.clock-skew-seconds:30}") + private long clockSkewSeconds = 30; + private static final Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512); // retrieve username from jwt token @@ -59,7 +63,12 @@ public class JwtTokenUtil implements Serializable { // for retrieveing any information from token we will need the secret key private Claims getAllClaimsFromToken(String token) { - return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody(); + return Jwts.parserBuilder() + .setSigningKey(secretKey) + .setAllowedClockSkewSeconds(clockSkewSeconds) + .build() + .parseClaimsJws(token) + .getBody(); } // check if the token has expired diff --git a/src/main/java/com/ffii/fpsms/config/security/jwt/JwtRequestFilter.java b/src/main/java/com/ffii/fpsms/config/security/jwt/JwtRequestFilter.java index 587c466..b2bcabf 100644 --- a/src/main/java/com/ffii/fpsms/config/security/jwt/JwtRequestFilter.java +++ b/src/main/java/com/ffii/fpsms/config/security/jwt/JwtRequestFilter.java @@ -46,7 +46,8 @@ public class JwtRequestFilter extends OncePerRequestFilter { try { username = jwtTokenUtil.getUsernameFromToken(jwtToken); } catch (ExpiredJwtException e) { - logger.error("JWT Token has expired", e); + // Expected when user leaves tab open; log at WARN without stack to avoid log noise + logger.warn("JWT Token has expired: {}", e.getMessage()); sendUnauthorizedJson(response, "JWT Token has expired", "TOKEN_EXPIRED"); return; } catch (JwtException | IllegalArgumentException e) { diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index d627e2f..0db8be1 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,6 +1,6 @@ -# Shorter session in production; frontend should call /refresh-token or re-login. +# Session length in production; frontend can call /refresh-token before expiry to stay logged in. jwt: - expiration-minutes: 30 # 30 min access token + expiration-minutes: 480 # 8 hours access token (was 30 min); increase if users need longer AFK refresh-expiration-days: 7 spring: