From 13d17923e47d650521ad6a641da3a419485673a5 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 30 Dec 2025 10:46:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(upms):=20=E5=A2=9E=E5=BC=BA=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1=E9=AA=8C=E8=AF=81=E7=A0=81=E6=B3=A8=E5=86=8C=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=92=8C=E5=AE=89=E5=85=A8=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在RegisterUserDTO中新增短信验证码字段 - 用户注册时增加短信验证码校验逻辑 - 合并短信验证码发送接口为单一API - 增加IP频率限制防止短信轰炸攻击 - 优化验证码校验失败的错误返回类型 --- .../pigx/admin/api/dto/RegisterUserDTO.java | 5 +++ .../controller/SysMessageController.java | 34 +++++++------------ .../service/impl/SysMessageServiceImpl.java | 21 ++++++++++-- .../service/impl/SysUserServiceImpl.java | 10 ++++++ 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java index fb2b73abe..9101f8a9a 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java @@ -36,4 +36,9 @@ public class RegisterUserDTO { */ private Long tenantId; + /** + * 短信验证码 + */ + private String code; + } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java index 978211887..79a2a0f67 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java @@ -182,26 +182,16 @@ public class SysMessageController { } /** - * 发送短信(验证码) + * 发送短信验证码 * - * @param mobile 手机号 (需要注册,对外提供,安全性第一) - * @return {@link R } + * @param mobile 手机号 + * @param registered 是否已注册,默认true + * @return 发送结果 */ @Inner(value = false) @GetMapping("/send/smsCode") - public R sendSmsCode(@RequestParam String mobile) { - return sysMessageService.sendSmsCode(mobile, true); - } - - /** - * 发送内部短信验证码 - * - * @param mobile 手机号码 (系统内不一定存在) - * @return 发送结果 - */ - @GetMapping("/send/smsInnerCode") - public R sendSmsInnerCode(@RequestParam String mobile) { - return sysMessageService.sendSmsCode(mobile, false); + public R sendSmsCode(@RequestParam String mobile, @RequestParam(required = false, defaultValue = "true") boolean registered) { + return sysMessageService.sendSmsCode(mobile, registered); } /** @@ -234,12 +224,12 @@ public class SysMessageController { */ @PostMapping("/send/hook") public R sendWebhook(@Valid @RequestBody MessageHookDTO messageHookDTO) { - return sysMessageService.sendHook(messageHookDTO); - } + return sysMessageService.sendHook(messageHookDTO); + } - @GetMapping("/list/hook") - public R listHookBizCode(MessageHookDTO messageHookDTO) { - return sysMessageService.listHookBizCode(messageHookDTO); - } + @GetMapping("/list/hook") + public R listHookBizCode(MessageHookDTO messageHookDTO) { + return sysMessageService.listHookBizCode(messageHookDTO); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java index 76da753d9..a0dbb1660 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java @@ -42,7 +42,9 @@ import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum; import com.pig4cloud.pigx.common.core.exception.CheckedException; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.core.util.WebUtils; import com.pig4cloud.pigx.common.data.cache.RedisUtils; +import com.pig4cloud.pigx.common.data.resolver.ParamResolver; import com.pig4cloud.pigx.common.data.tenant.TenantBroker; import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; import com.pig4cloud.pigx.common.file.core.FileTemplate; @@ -279,12 +281,27 @@ public class SysMessageServiceImpl extends ServiceImpl sendSmsCode(String mobile, boolean registered) { + // IP 限制校验,单位时间内同一IP发送次数限制 + String clientIp = WebUtils.getIP(); + String ipLimitKey = CacheConstants.DEFAULT_CODE_KEY + "IP_LIMIT:" + clientIp; + if (StrUtil.isNotBlank(clientIp)) { + Long maxTimes = ParamResolver.getLong("SMS_IP_LIMIT_TIMES", 5L); + Long ipSendTimes = RedisUtils.increment(ipLimitKey, 1L); + // 设置过期时间 + RedisUtils.expire(ipLimitKey, SecurityConstants.CODE_TIME); + + if (ipSendTimes > maxTimes) { + log.info("IP发送验证码过于频繁:{}, 已发送{}次", clientIp, ipSendTimes); + return R.failed(Boolean.FALSE, MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_OFTEN)); + } + } + List userList = TenantBroker .noneAs(() -> userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getPhone, mobile))); if (registered && CollUtil.isEmpty(userList)) { log.info("手机号未注册:{}", mobile); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); + return R.failed(Boolean.FALSE, MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); } String codeObj = RedisUtils @@ -292,7 +309,7 @@ public class SysMessageServiceImpl extends ServiceImpl impl @Override @Transactional(rollbackFor = Exception.class) public R registerUser(RegisterUserDTO userDto) { + // 校验短信验证码 + if (StrUtil.isBlank(userDto.getCode())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_ILLEGAL)); + } + String codeObj = RedisUtils + .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone()); + if (!StrUtil.equals(codeObj, userDto.getCode())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR)); + } + // 判断用户名是否存在 boolean usernameExists = TenantBroker .noneAs(() -> this.exists(Wrappers.lambdaQuery().eq(SysUser::getUsername, userDto.getUsername())));