jeecgboot/JeecgBoot · warning · JeecgBootException
40002
40002
Error message
短信接口请求太多,请稍后再试!
What it means
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.
Source
Thrown at jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java:2200
redisUtil.removeAll(code);
redisUtil.removeAll(CacheConstant.SYS_USERS_CACHE + phone);
}
/**
* 发送短信验证码
* @param phone
*/
private void sendPhoneSms(String phone, String clientIp,String redisKey) {
Object object = redisUtil.get(redisKey);
if (object != null) {
throw new JeecgBootException("验证码10分钟内,仍然有效!");
}
//增加 check防止恶意刷短信接口
if(!DySmsLimit.canSendSms(clientIp)){
log.warn("--------[警告] IP地址:{}, 短信接口请求太多-------", clientIp);
throw new JeecgBootException("短信接口请求太多,请稍后再试!", CommonConstant.PHONE_SMS_FAIL_CODE);
}
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
obj.put("code", captcha);
try {
boolean sendSmsSuccess = DySmsHelper.sendSms(phone, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);
if(!sendSmsSuccess){
throw new JeecgBootException("短信验证码发送失败,请稍后重试!");
}
//验证码10分钟内有效
redisUtil.set(redisKey, captcha, 600);
} catch (ClientException e) {
log.error(e.getMessage(),e);
throw new JeecgBootException("短信接口未配置,请联系管理员!");
}
}View on GitHub (pinned to 96fb33f5ec)
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.
Example fix
// before: IP-only limit, in-memory blacklist survives only until restart
if (!DySmsLimit.canSendSms(clientIp)) {
throw new JeecgBootException("短信接口请求太多,请稍后再试!", CommonConstant.PHONE_SMS_FAIL_CODE);
}
// after: phone+IP sliding window in Redis with a TTL and no permanent blacklist
String key = "sms:limit:" + phone + ":" + clientIp;
Long count = redisUtil.incr(key, 1L);
if (count == 1L) { redisUtil.expire(key, 60); }
if (count > 5) {
throw new JeecgBootException("短信接口请求太多,请稍后再试!", CommonConstant.PHONE_SMS_FAIL_CODE);
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-check the in-memory limiter before calling sendPhoneSms
if (!DySmsLimit.canSendSms(clientIp)) {
// show 'try again later' to the user; do NOT retry immediately
return Result.error(CommonConstant.PHONE_SMS_FAIL_CODE, "短信接口请求太多,请稍后再试");
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/1044b7037d3f6d4f.
Report an issue: GitHub.