paascloud/paascloud-master · warning · ValidateCodeException

操作频率过快

Error message

操作频率过快

What it means

SmsCodeProcessor.checkSendSmsCount enforces send-rate limiting before dispatching an SMS. If a rate key for the requester's IP exists in Redis (set for 1 minute after each send), the send is refused with ValidateCodeException '操作频率过快'.

Solutions

  1. Wait 1 minute (rate window) before requesting again
  2. Debounce/disable the send button on the client after the first click and show a countdown
  3. Tune rate limits via securityProperties.getCode().getSms() (send rate interval, mobileMaxSendCount)
  4. Catch ValidateCodeException and surface a friendly 'please retry later' message

Example fix

// before
sendButton.onclick = () => requestSmsCode();
// after
sendButton.onclick = () => {
  if (countdown > 0) return;
  requestSmsCode();
  startCountdown(60);
};
Defensive patterns

Strategy: try-catch

Validate before calling

if (Date.now() - lastSendAt < 60000) { alert('操作过于频繁,请稍后再试'); return; }

Try / catch

try { smsProcessor.send(request); } catch (ValidateCodeException e) { if (e.getMessage().contains("操作频率过快")) { return ResponseEntity.status(429).body("请一分钟后再试"); } throw e; }

Prevention

When it happens

Trigger: Requesting an SMS code more than once within the configured rate window (default 1 minute per IP), or exceeding mobileMaxSendCount within its window; raised from send() via checkSendSmsCount.

Common situations: Users double-clicking the send button; automated scripts hammering the endpoint; shared NAT/office IP causing legitimate distinct users to hit the same IP counter; retry loops in a buggy frontend.

Related errors


AI-assisted analysis of paascloud/paascloud-master@781281a950 (2026-09-10). Data as JSON: /api/errors/fbaa015e5b1e4f62. Report an issue: GitHub.

Appendix: source

Thrown at paascloud-common/paascloud-security-core/src/main/java/com/paascloud/security/core/validate/code/sms/SmsCodeProcessor.java:108

			result = SecurityResult.error("内部异常", false);
		}
		String json = objectMapper.writeValueAsString(result);
		HttpServletResponse response = request.getResponse();
		response.setCharacterEncoding("UTF-8");
		response.getWriter().write(json);
	}

	private void checkSendSmsCount(String mobile, String ipAddr) {
		String mobileSmsCountKey = RedisKeyUtil.getSendSmsCountKey(mobile, "mobile");
		String ipSmsCountKey = RedisKeyUtil.getSendSmsCountKey(ipAddr, "ip");
		String totalSmsCountKey = RedisKeyUtil.getSendSmsCountKey("total", "total");
		String sendSmsRateKey = RedisKeyUtil.getSendSmsRateKey(ipAddr);
		SmsCodeProperties sms = securityProperties.getCode().getSms();

		Integer sendSmsRateCount = (Integer) redisTemplate.opsForValue().get(sendSmsRateKey);
		if (sendSmsRateCount != null) {
			log.error("操作频率过快 ipAddr={}, mobile={}", ipAddr, mobile);
			throw new ValidateCodeException("操作频率过快");
		} else {
			redisTemplate.opsForValue().set(sendSmsRateKey, 1, 1, TimeUnit.MINUTES);
		}

		Integer mobileSmsCount = (Integer) redisTemplate.opsForValue().get(mobileSmsCountKey);
		if (mobileSmsCount != null && mobileSmsCount > sms.getMobileMaxSendCount()) {
			log.error("Mobile当天短信发送数上限 ipAddr={}, mobile={}", ipAddr, mobile);
			throw new ValidateCodeException("Mobile当天短信发送数上限");
		} else {
			redisTemplate.opsForValue().set(mobileSmsCountKey, mobileSmsCount == null ? 1 : mobileSmsCount + 1, 1, TimeUnit.DAYS);
		}
		Integer ipSmsCount = (Integer) redisTemplate.opsForValue().get(ipSmsCountKey);
		if (ipSmsCount != null && ipSmsCount > sms.getIpMaxSendCount()) {
			log.error("IP当天短信发送数上限 ipAddr={}, mobile={}", ipAddr, mobile);
			throw new ValidateCodeException("IP当天短信发送数上限");
		} else {
			redisTemplate.opsForValue().set(ipSmsCountKey, ipSmsCount == null ? 1 : ipSmsCount + 1, 1, TimeUnit.DAYS);
		}

View on GitHub (pinned to 781281a950)