{"record":{"id":"1044b7037d3f6d4f","repo":"jeecgboot/JeecgBoot","slug":"40002","errorCode":"40002","errorMessage":"短信接口请求太多，请稍后再试！","messagePattern":"短信接口请求太多，请稍后再试！","errorType":"exception","errorClass":"JeecgBootException","httpStatus":null,"severity":"warning","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java","lineNumber":2200,"sourceCode":"\t\tredisUtil.removeAll(code);\n\t\tredisUtil.removeAll(CacheConstant.SYS_USERS_CACHE + phone);\n\t}\n\n\t/**\n\t * 发送短信验证码\n\t * @param phone\n\t */\n\tprivate void sendPhoneSms(String phone, String clientIp,String redisKey) {\n\t\tObject object = redisUtil.get(redisKey);\n\n\t\tif (object != null) {\n\t\t\tthrow new JeecgBootException(\"验证码10分钟内，仍然有效！\");\n\t\t}\n\n\t\t//增加 check防止恶意刷短信接口\n\t\tif(!DySmsLimit.canSendSms(clientIp)){\n\t\t\tlog.warn(\"--------[警告] IP地址:{}, 短信接口请求太多-------\", clientIp);\n\t\t\tthrow new JeecgBootException(\"短信接口请求太多，请稍后再试！\", CommonConstant.PHONE_SMS_FAIL_CODE);\n\t\t}\n\t\t\n\t\t//随机数\n\t\tString captcha = RandomUtil.randomNumbers(6);\n\t\tJSONObject obj = new JSONObject();\n\t\tobj.put(\"code\", captcha);\n\t\ttry {\n\t\t\tboolean sendSmsSuccess = DySmsHelper.sendSms(phone, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);\n\t\t\tif(!sendSmsSuccess){\n\t\t\t\tthrow new JeecgBootException(\"短信验证码发送失败,请稍后重试！\");\n\t\t\t}\n\t\t\t//验证码10分钟内有效\n\t\t\tredisUtil.set(redisKey, captcha, 600);\n\t\t} catch (ClientException e) {\n\t\t\tlog.error(e.getMessage(),e);\n\t\t\tthrow new JeecgBootException(\"短信接口未配置，请联系管理员！\");\n\t\t}\n\t}","sourceCodeStart":2182,"sourceCodeEnd":2218,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java#L2182-L2218","documentation":"Thrown by sendPhoneSms (code 40002 = CommonConstant.PHONE_SMS_FAIL_CODE) when DySmsLimit.canSendSms(clientIp) returns false. DySmsLimit enforces per-IP limits in a static ConcurrentHashMap: more than 5 sends within 60s is blocked, and reaching 20 sends within 60s permanently blacklists the IP for the process lifetime. The blacklist is in-memory only, so a restart clears it.","triggerScenarios":"A single client IP sends a 6th SMS request inside one rolling minute; an IP that previously hit 20/min remains in ipBlacklist and is denied on every subsequent call until JVM restart.","commonSituations":"Multiple users behind one corporate NAT/proxy sharing an IP; load test or automated UI suite hammering the endpoint; a developer sharing localhost with parallel test sessions.","solutions":["Wait at least 60 seconds for the per-minute counter to reset (the counter resets when currentTime - lastRequestTime >= 60000).","If blacklisted (>=20/min), restart the application JVM or call DySmsLimit.clearSendSmsCount(ip) — note clearSendSmsCount resets the count but not the blacklist flag, so a restart may be required.","When many legitimate users share one IP, replace DySmsLimit with a Redis-backed limiter keyed on phone+IP rather than IP alone, and raise MAX_MESSAGE_PER_MINUTE.","Ensure the client passes the real clientIp (resolved from request) and not a constant placeholder so the limit is per real client."],"exampleFix":"// before: IP-only limit, in-memory blacklist survives only until restart\nif (!DySmsLimit.canSendSms(clientIp)) {\n    throw new JeecgBootException(\"短信接口请求太多，请稍后再试！\", CommonConstant.PHONE_SMS_FAIL_CODE);\n}\n// after: phone+IP sliding window in Redis with a TTL and no permanent blacklist\nString key = \"sms:limit:\" + phone + \":\" + clientIp;\nLong count = redisUtil.incr(key, 1L);\nif (count == 1L) { redisUtil.expire(key, 60); }\nif (count > 5) {\n    throw new JeecgBootException(\"短信接口请求太多，请稍后再试！\", CommonConstant.PHONE_SMS_FAIL_CODE);\n}","handlingStrategy":"validation","validationCode":"// Pre-check the in-memory limiter before calling sendPhoneSms\nif (!DySmsLimit.canSendSms(clientIp)) {\n    // show 'try again later' to the user; do NOT retry immediately\n    return Result.error(CommonConstant.PHONE_SMS_FAIL_CODE, \"短信接口请求太多，请稍后再试\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Throttle the client to <5 sends/min per IP and add a UI countdown.","For shared-IP deployments, move the limiter to Redis keyed on phone+IP and raise the threshold.","Remember the in-memory blacklist survives only until JVM restart; restart to clear a false positive."],"tags":["sms","rate-limit","ip-blacklist","jeecg-boot"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}