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

  1. Wait at least 60 seconds for the per-minute counter to reset (the counter resets when currentTime - lastRequestTime >= 60000).
  2. 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.
  3. 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.
  4. 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

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.