xkcoding/spring-boot-demo · error · SecurityException
5002
5002
Error message
token 已过期,请重新登录!
What it means
parseJWT checks the Redis TTL of key Consts.REDIS_JWT_KEY_PREFIX+username; if stringRedisTemplate.getExpire returns null or <= 0 it throws SecurityException(Status.TOKEN_EXPIRED) (README:226; impl JwtUtil.java:97-98). This is the REDIS-side expiry, separate from the JWT exp claim - the token may still be cryptographically valid but is treated as expired because Redis no longer holds it.
Source
Thrown at demo-rbac-security/README.md:226
* 解析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);View on GitHub (pinned to 87a142f960)
Solutions
- Enable Redis persistence (AOF/RDB) if tokens must survive restarts
- Prompt re-login to repopulate the Redis JWT key via createJWT
- Keep the Redis ttl aligned with the JWT exp ttl set in createJWT
- Avoid flushing the JWT keyspace in shared environments
Defensive patterns
Strategy: validation
Validate before calling
Long expire = stringRedisTemplate.getExpire(Consts.REDIS_JWT_KEY_PREFIX + username, TimeUnit.MILLISECONDS);
if (expire == null || expire <= 0) {
// Redis no longer holds the token - force re-login before trusting the JWT
} Try / catch
try { jwtUtil.parseJWT(token); }
catch (SecurityException e) { if (Status.TOKEN_EXPIRED.getCode().equals(e.getCode())) { /* Redis TTL expired - re-login */ } } Prevention
- Enable Redis persistence so JWT keys survive restarts
- Keep the Redis ttl equal to the JWT exp ttl
- Never FLUSHDB on a Redis instance holding live sessions
When it happens
Trigger: Redis evicted or expired the JWT key (its ttl elapsed) before the JWT exp claim expired; Redis was flushed or restarted without persistence; invalidateJWT deleted the key on logout.
Common situations: Redis restart without AOF/RDB; a manual FLUSHDB; the Redis ttl set shorter than the JWT exp ttl; logout performed on another device.
Related errors
AI-assisted analysis of xkcoding/spring-boot-demo@87a142f960 (2026-08-14).
Data as JSON: /api/errors/1e2d3c959bb44b61.
Report an issue: GitHub.