wuyouzhuguli/SpringAll · error · ValidateCodeException

验证码已过期!

Error message

验证码已过期!

What it means

ValidateCodeException thrown by SmsCodeFilter.validateCode() when the session SmsCode is present but expired (isExpire() true). NOTE: the expiry-cleanup path has a copy-paste defect - line 57 removes ValidateController.SESSION_KEY_IMAGE_CODE (the image-captcha key) instead of SESSION_KEY_SMS_CODE + mobileInRequest, so it neither clears the expired SMS code nor leaves the image session intact. Combined with the line-46 bug, the lookup key itself is often wrong.

Source

Thrown at 38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsCodeFilter.java:58

        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {
        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "smsCode");
        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "smsCode");

        SmsCode codeInSession = (SmsCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest);

        if (StringUtils.isBlank(smsCodeInRequest)) {
            throw new ValidateCodeException("验证码不能为空!");
        }
        if (codeInSession == null) {
            throw new ValidateCodeException("验证码不存在!");
        }
        if (codeInSession.isExpire()) {
            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);
            throw new ValidateCodeException("验证码已过期!");
        }
        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), smsCodeInRequest)) {
            throw new ValidateCodeException("验证码不正确!");
        }
        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);

    }
}

View on GitHub (pinned to 614d2578d9)

Solutions

  1. Fix line 57 to remove ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest, not SESSION_KEY_IMAGE_CODE.
  2. Also fix line 46 so mobileInRequest is read from the 'mobile' parameter.
  3. Refresh the SMS code and resubmit within the TTL.
  4. Increase expireIn if legitimate users cannot submit in time.

Example fix

// before (line 57)
sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);

// after
sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest);
Defensive patterns

Strategy: retry

Validate before calling

// client-side: refresh the SMS code and track its lifetime
async function refreshSmsCode(mobile) {
    await fetch('/code/sms?mobile=' + encodeURIComponent(mobile), { credentials: 'same-origin' });
    smsGeneratedAt = Date.now();
}
if (Date.now() - smsGeneratedAt > SMS_TTL_MS) await refreshSmsCode(mobile);

Prevention

When it happens

Trigger: POST to /login/mobile with an SMS code whose age exceeds the configured expireIn; effectively reachable only after the line-46 mobile bug is fixed so the correct session key is consulted.

Common situations: User let the SMS code expire before submitting; expireIn configured too short; the wrong session attribute is being cleared (line 57 bug), leaving stale codes; mobile-key bug masks this branch entirely.

Related errors


AI-assisted analysis of wuyouzhuguli/SpringAll@614d2578d9 (2026-08-14). Data as JSON: /api/errors/3561afeb520bdc49. Report an issue: GitHub.