wuyouzhuguli/SpringAll · warning · Exception
验证码已过期!
Error message
验证码已过期!
What it means
Generic Exception thrown when redisCodeService.get returns null — i.e. no SMS code under 'SMS_CODE:deviceId:mobile' in Redis. Codes are stored with a 300s TTL (RedisCodeService.TIME_OUT). Reaching here means the code was never saved, already expired/removed, or the key components (deviceId, mobile) differ between save and validate.
Source
Thrown at 64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsCodeFilter.java:52
} catch (Exception e) {
authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new AuthenticationServiceException(e.getMessage()));
return;
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
private void validateCode(ServletWebRequest servletWebRequest) throws Exception {
String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "smsCode");
String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "mobile");
String codeInRedis = redisCodeService.get(servletWebRequest, mobileInRequest);
if (StringUtils.isBlank(smsCodeInRequest)) {
throw new Exception("验证码不能为空!");
}
if (codeInRedis == null) {
throw new Exception("验证码已过期!");
}
if (!StringUtils.equalsIgnoreCase(codeInRedis, smsCodeInRequest)) {
throw new Exception("验证码不正确!");
}
redisCodeService.remove(servletWebRequest, mobileInRequest);
}
}View on GitHub (pinned to 614d2578d9)
Solutions
- Call GET /code/sms?mobile=<num> with the same deviceId header before /login/mobile, and submit within 300s.
- Use a stable, persisted deviceId across both requests.
- Normalize the mobile string (strip +86, spaces, dashes) identically on save and validate.
- If TTL is too short, raise RedisCodeService.TIME_OUT.
Defensive patterns
Strategy: retry
Validate before calling
// Ensure an SMS code exists in Redis for this device+mobile before login.
// Call GET /code/sms?mobile=<num> with the same deviceId, then submit within 300s.
await fetch(`/code/sms?mobile=${mobile}`, { headers:{ deviceId } }); Try / catch
try { await smsLogin(); }
catch (e) {
if (/已过期/.test(e.message)) { await resendSms(); /* then resubmit */ }
else handleError(e);
} Prevention
- Use a stable deviceId across /code/sms and /login/mobile.
- Normalize the mobile string identically on both calls.
- Submit within the 300s Redis TTL; raise TIME_OUT if needed.
- Verify the Redis key matches by logging it during debugging.
When it happens
Trigger: GET /code/sms was never called for this mobile+deviceId; the Redis key expired (>300s); a different deviceId header was used at generation vs login; the mobile differs between the two calls.
Common situations: deviceId not stable across requests (regenerated per launch); user waited over 5 minutes; Redis was flushed/restarted; mobile formatting differs (+86 prefix, spaces).
Related errors
AI-assisted analysis of wuyouzhuguli/SpringAll@614d2578d9 (2026-08-14).
Data as JSON: /api/errors/d2abc1002a7d26e3.
Report an issue: GitHub.