xkcoding/spring-boot-demo · warning · SecurityException
5002
5002
Error message
token 已过期,请重新登录!
What it means
Thrown by JwtUtil.parseJWT when the JWT is considered expired. Two distinct paths produce this: (1) the Redis key holding the JWT (REDIS_JWT_KEY_PREFIX + username) has expired or been deleted (getExpire returns null or <= 0), or (2) the jjwt library's parseClaimsJws throws ExpiredJwtException because the token's own exp claim has passed. Status.TOKEN_EXPIRED (code 5002) wraps the error. The Redis check is a secondary expiry mechanism layered on top of the JWT's built-in expiration.
Source
Thrown at demo-rbac-security/src/main/java/com/xkcoding/rbac/security/util/JwtUtil.java:98
}
/**
* 解析JWT
*
* @param jwt JWT
* @return {@link Claims}
*/
public Claims parseJWT(String jwt) {
try {
Claims claims = Jwts.parser().setSigningKey(jwtConfig.getKey()).parseClaimsJws(jwt).getBody();
String username = claims.getSubject();
String redisKey = Consts.REDIS_JWT_KEY_PREFIX + username;
// 校验redis中的JWT是否存在
Long expire = stringRedisTemplate.getExpire(redisKey, TimeUnit.MILLISECONDS);
if (Objects.isNull(expire) || expire <= 0) {
throw new SecurityException(Status.TOKEN_EXPIRED);
}
// 校验redis中的JWT是否与当前的一致,不一致则代表用户已注销/用户在不同设备登录,均代表JWT已过期
String redisToken = stringRedisTemplate.opsForValue().get(redisKey);
if (!StrUtil.equals(jwt, redisToken)) {
throw new SecurityException(Status.TOKEN_OUT_OF_CTRL);
}
return claims;
} catch (ExpiredJwtException e) {
log.error("Token 已过期");
throw new SecurityException(Status.TOKEN_EXPIRED);
} catch (UnsupportedJwtException e) {
log.error("不支持的 Token");
throw new SecurityException(Status.TOKEN_PARSE_ERROR);
} catch (MalformedJwtException e) {
log.error("Token 无效");
throw new SecurityException(Status.TOKEN_PARSE_ERROR);
} catch (SignatureException e) {View on GitHub (pinned to 87a142f960)
Solutions
- On the client, catch the 5002 response and redirect to the login page to obtain a fresh JWT.
- Verify Redis connectivity — if Redis is unreachable, all tokens appear expired.
- Check jwtConfig.ttl and jwtConfig.remember values in application.yml are appropriate for the use case.
- Ensure the Redis key prefix (Consts.REDIS_JWT_KEY_PREFIX) is consistent across create and parse operations.
Defensive patterns
Strategy: try-catch
Validate before calling
// Client-side: check JWT expiry before making authenticated requests
// Decode the exp claim (no signature verification needed for client-side check)
try {
Claims claims = Jwts.parser().parseClaimsJwt(jwt.split("\\.")[0] + "." + jwt.split("\\.")[1] + ".").getBody();
if (claims.getExpiration().before(new Date())) {
// Token expired — refresh or redirect to login
redirectToLogin();
return;
}
} catch (Exception e) {
redirectToLogin();
} Try / catch
// Client-side: intercept 5002 token-expired responses
try {
apiClient.someProtectedResource();
} catch (SecurityException e) {
if (e.getStatus().getCode() == 5002) {
// Token expired — redirect to login for a new token
redirectToLogin();
}
} Prevention
- Implement a token-refresh mechanism or silent re-authentication before the JWT TTL expires.
- Monitor Redis connectivity — Redis failure causes all tokens to appear expired.
- Keep jwtConfig.ttl and jwtConfig.remember values consistent with user-experience expectations.
- Ensure the Redis key prefix is identical across createJWT and parseJWT.
When it happens
Trigger: Any authenticated request whose JWT has exceeded its TTL (jwtConfig.ttl or jwtConfig.remember for 'remember me'), or whose Redis backing key has been removed (e.g., by a prior logout). Also fires if the Redis server is down and getExpire returns null.
Common situations: Normal session timeout after the configured TTL; user logged out from another device (Redis key deleted); Redis connection failure causing getExpire to return null; clock skew between token issuance and validation servers; jwtConfig.ttl set very short.
Related errors
AI-assisted analysis of xkcoding/spring-boot-demo@87a142f960 (2026-08-14).
Data as JSON: /api/errors/1a214874a7f1b606.
Report an issue: GitHub.