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

  1. Call GET /code/sms?mobile=<num> with the same deviceId header before /login/mobile, and submit within 300s.
  2. Use a stable, persisted deviceId across both requests.
  3. Normalize the mobile string (strip +86, spaces, dashes) identically on save and validate.
  4. 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

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.