{"record":{"id":"20ae2286ed1a21f1","repo":"paascloud/paascloud-master","slug":"mobile","errorCode":null,"errorMessage":"Mobile当天短信发送数上限","messagePattern":"Mobile当天短信发送数上限","errorType":"error_code","errorClass":"ValidateCodeException","httpStatus":null,"severity":"warning","filePath":"paascloud-common/paascloud-security-core/src/main/java/com/paascloud/security/core/validate/code/sms/SmsCodeProcessor.java","lineNumber":116,"sourceCode":"\tprivate void checkSendSmsCount(String mobile, String ipAddr) {\n\t\tString mobileSmsCountKey = RedisKeyUtil.getSendSmsCountKey(mobile, \"mobile\");\n\t\tString ipSmsCountKey = RedisKeyUtil.getSendSmsCountKey(ipAddr, \"ip\");\n\t\tString totalSmsCountKey = RedisKeyUtil.getSendSmsCountKey(\"total\", \"total\");\n\t\tString sendSmsRateKey = RedisKeyUtil.getSendSmsRateKey(ipAddr);\n\t\tSmsCodeProperties sms = securityProperties.getCode().getSms();\n\n\t\tInteger sendSmsRateCount = (Integer) redisTemplate.opsForValue().get(sendSmsRateKey);\n\t\tif (sendSmsRateCount != null) {\n\t\t\tlog.error(\"操作频率过快 ipAddr={}, mobile={}\", ipAddr, mobile);\n\t\t\tthrow new ValidateCodeException(\"操作频率过快\");\n\t\t} else {\n\t\t\tredisTemplate.opsForValue().set(sendSmsRateKey, 1, 1, TimeUnit.MINUTES);\n\t\t}\n\n\t\tInteger mobileSmsCount = (Integer) redisTemplate.opsForValue().get(mobileSmsCountKey);\n\t\tif (mobileSmsCount != null && mobileSmsCount > sms.getMobileMaxSendCount()) {\n\t\t\tlog.error(\"Mobile当天短信发送数上限 ipAddr={}, mobile={}\", ipAddr, mobile);\n\t\t\tthrow new ValidateCodeException(\"Mobile当天短信发送数上限\");\n\t\t} else {\n\t\t\tredisTemplate.opsForValue().set(mobileSmsCountKey, mobileSmsCount == null ? 1 : mobileSmsCount + 1, 1, TimeUnit.DAYS);\n\t\t}\n\t\tInteger ipSmsCount = (Integer) redisTemplate.opsForValue().get(ipSmsCountKey);\n\t\tif (ipSmsCount != null && ipSmsCount > sms.getIpMaxSendCount()) {\n\t\t\tlog.error(\"IP当天短信发送数上限 ipAddr={}, mobile={}\", ipAddr, mobile);\n\t\t\tthrow new ValidateCodeException(\"IP当天短信发送数上限\");\n\t\t} else {\n\t\t\tredisTemplate.opsForValue().set(ipSmsCountKey, ipSmsCount == null ? 1 : ipSmsCount + 1, 1, TimeUnit.DAYS);\n\t\t}\n\t\tInteger totalSmsCount = (Integer) redisTemplate.opsForValue().get(totalSmsCountKey);\n\t\tif (totalSmsCount != null && totalSmsCount > sms.getTotalMaxSendCount()) {\n\t\t\tlog.error(\"当天短信发送数上限 ipAddr={}, mobile={}\", ipAddr, mobile);\n\t\t\tthrow new ValidateCodeException(\"当天短信发送数上限\");\n\t\t} else {\n\t\t\tredisTemplate.opsForValue().set(totalSmsCountKey, totalSmsCount == null ? 1 : totalSmsCount + 1, 1, TimeUnit.DAYS);\n\t\t}\n\t}","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/paascloud/paascloud-master/blob/781281a9503332ed3cef44ea618349d14230a127/paascloud-common/paascloud-security-core/src/main/java/com/paascloud/security/core/validate/code/sms/SmsCodeProcessor.java#L98-L134","documentation":"SmsCodeProcessor.checkSendSmsCount enforces per-mobile daily SMS quotas by reading a Redis counter (mobileSmsCountKey, 1-day TTL) and comparing it to sms.getMobileMaxSendCount(). If the counter exceeds the configured mobile daily limit, it throws ValidateCodeException('Mobile当天短信发送数上限'). This is a deliberate anti-abuse throttle to stop a single phone number from exhausting SMS quota or receiving too many codes.","triggerScenarios":"Calling the SMS send flow (send -> checkSendSmsCount) for a mobile number whose Redis day-counter already exceeds smsProperties.getMobileMaxSendCount(); the counter increments on every send and lives 1 day, so repeated sends to the same phone within 24h trip it.","commonSituations":"QA/load tests hammering one phone number; users repeatedly requesting codes during login failures; mobileMaxSendCount configured too low in properties for legitimate traffic; integration tests not resetting the Redis counter between runs.","solutions":["Wait for the 1-day Redis key (mobile SMS count) to expire, or delete the key (e.g. DEL <mobileSmsCountKey>) to reset the quota for testing","Raise sms.getMobileMaxSendCount() in the security code SMS properties configuration to a value fitting real traffic","In tests, use different phone numbers per run or flush the Redis counter before each test","Catch ValidateCodeException in the controller and return a friendly 'daily SMS limit reached, try tomorrow' message"],"exampleFix":"// before: repeated test sends hit the limit\nsmsCodeSender.send(mobile); // throws ValidateCodeException after N sends\n// after: reset the counter in test setup\n@Test\nvoid setUp() {\n    redisTemplate.delete(\"paascloud:sms:count:\" + mobile);\n}","handlingStrategy":"try-catch","validationCode":"Integer count = (Integer) redisTemplate.opsForValue().get(mobileSmsCountKey);\nif (count != null && count > maxMobileSendCount) {\n    throw new BizException(\"SMS daily limit reached for this phone\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    smsCodeSender.send(mobile);\n} catch (ValidateCodeException e) {\n    if (e.getMessage().contains(\"上限\")) {\n        return Result.error(429, \"短信发送次数已达今日上限，请明日再试\");\n    }\n    throw e;\n}","preventionTips":["Check the mobile's Redis counter before offering the 'send code' UI action and disable the button when at limit","Configure mobileMaxSendCount realistically for legitimate users (e.g. 10/day), not the minimum","In CI/integration tests, flush SMS counter keys or use distinct phone numbers per run","Return HTTP 429 semantics to clients so they can implement backoff"],"tags":["rate-limit","sms","redis","security"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"781281a9503332ed3cef44ea618349d14230a127","analyzedAt":"2026-09-10T10:59:02.070Z","contentChangedAt":"2026-09-10T10:59:02.070Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}