{"record":{"id":"d2abc1002a7d26e3","repo":"wuyouzhuguli/SpringAll","slug":"error-d2abc1","errorCode":null,"errorMessage":"验证码已过期！","messagePattern":"验证码已过期！","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"warning","filePath":"64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsCodeFilter.java","lineNumber":52,"sourceCode":"            } catch (Exception e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new AuthenticationServiceException(e.getMessage()));\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws Exception {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"mobile\");\n\n        String codeInRedis = redisCodeService.get(servletWebRequest, mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new Exception(\"验证码不能为空！\");\n        }\n        if (codeInRedis == null) {\n            throw new Exception(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInRedis, smsCodeInRequest)) {\n            throw new Exception(\"验证码不正确！\");\n        }\n        redisCodeService.remove(servletWebRequest, mobileInRequest);\n\n    }\n}","sourceCodeStart":34,"sourceCodeEnd":60,"githubUrl":"https://github.com/wuyouzhuguli/SpringAll/blob/614d2578d9495acf53cc02f2dee9c6131cc5e51a/64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsCodeFilter.java#L34-L60","documentation":"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.","triggerScenarios":"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.","commonSituations":"deviceId not stable across requests (regenerated per launch); user waited over 5 minutes; Redis was flushed/restarted; mobile formatting differs (+86 prefix, spaces).","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Ensure an SMS code exists in Redis for this device+mobile before login.\n// Call GET /code/sms?mobile=<num> with the same deviceId, then submit within 300s.\nawait fetch(`/code/sms?mobile=${mobile}`, { headers:{ deviceId } });","typeGuard":null,"tryCatchPattern":"try { await smsLogin(); }\ncatch (e) {\n  if (/已过期/.test(e.message)) { await resendSms(); /* then resubmit */ }\n  else handleError(e);\n}","preventionTips":["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."],"tags":["sms","redis","captcha","expiry","oauth2"],"backgroundTag":null,"analyzedSha":"614d2578d9495acf53cc02f2dee9c6131cc5e51a","analyzedAt":"2026-08-14T04:40:03.488Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}