From aafb2ef1138c50e7afc69fae1fc737cd8cd2399a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Tue, 24 Mar 2026 22:05:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(sms):=20=E4=BF=AE=E5=A4=8D=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1=E9=AA=8C=E8=AF=81=E7=A0=81=E5=8F=91=E9=80=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E7=9A=84=E5=AE=89=E5=85=A8=E6=80=A7=E5=92=8C=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加了参数校验,防止空参数导致的异常 - 替换了硬编码的阿里云密钥配置,支持租户自定义配置 - 修复了随机数生成器的安全问题,使用ThreadLocalRandom替代Random - 添加了日志记录功能,便于问题排查和监控 - 优化了Redis缓存键的存储逻辑,兼容历史数据格式 - 增强了异常处理机制,提供更详细的错误信息反馈 - 修复了短信模板参数格式问题,确保验证码正确传递 - 添加了手机号脱敏处理,保护用户隐私安全 --- .../system/controller/MainController.java | 97 +++++++++++++------ 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/gxwebsoft/common/system/controller/MainController.java b/src/main/java/com/gxwebsoft/common/system/controller/MainController.java index 0b11e4d..185170d 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/MainController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/MainController.java @@ -47,6 +47,7 @@ import com.wf.captcha.SpecCaptcha; import io.jsonwebtoken.Claims; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; @@ -61,8 +62,8 @@ import java.time.Instant; import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; -import java.util.Random; import java.util.concurrent.TimeUnit; +import java.util.concurrent.ThreadLocalRandom; import static com.gxwebsoft.common.core.constants.WebsiteConstants.CACHE_KEY_UNIVERSAL_PASSWORD; import static com.gxwebsoft.common.core.constants.WebsiteConstants.CACHE_KEY_VERIFICATION_CODE_BY_DEV_SMS; @@ -73,6 +74,7 @@ import static com.gxwebsoft.common.core.constants.WebsiteConstants.CACHE_KEY_VER * @author WebSoft * @since 2018-12-24 16:10:11 */ +@Slf4j @Tag(name = "登录认证") @RestController @RequestMapping("/api") @@ -441,11 +443,15 @@ public class MainController extends BaseController { @Operation(summary = "发送短信验证码") @PostMapping("/sendSmsCaptcha") public ApiResult sendSmsCaptcha(@RequestBody SmsCaptchaParam param) { - // 默认配置 - String accessKeyId = "LTAI5tEsyhW4GCKbds1qsopg"; - String accessKeySecret = "zltFlQrYVAoq2KMFDWgLa3GhkMNeyO"; - String userTemplateId = "SMS_481670203"; - String sign = "网宿信息"; + if (param == null) { + return fail("参数不能为空"); + } + // 默认配置(当租户未配置短信服务时使用) + String accessKeyId = "LTAI5t7jGTFTbpSLzzXY8HzP"; + String accessKeySecret = "Z22EPJyUhQaIZfEEmZ4Hdbw6xZibCb"; + String templateCode = "SMS_481670203"; + String signName = "网宿信息"; + String regionId = "cn-hangzhou"; if (!CommonUtil.isValidPhoneNumber(param.getPhone())) { return fail("请输入有效的手机号码"); @@ -459,53 +465,90 @@ public class MainController extends BaseController { } - // 读取租户的短信配置 - if (getTenantId() != null) { - String string = redisUtil.get("setting:sms:" + getTenantId()); - if (string != null) { - JSONObject jsonObject = JSONObject.parseObject(string); - accessKeyId = jsonObject.getString("accessKeyId"); - accessKeySecret = jsonObject.getString("accessKeySecret"); - userTemplateId = jsonObject.getString("userTemplateId"); - sign = jsonObject.getString("sign"); + Integer tenantId = getTenantId(); + if (tenantId == null && StrUtil.isNotBlank(param.getTenantId())) { + try { + tenantId = Integer.valueOf(param.getTenantId()); + } catch (NumberFormatException e) { + return fail("租户ID格式不正确"); } } - DefaultProfile profile = DefaultProfile.getProfile("regionld", accessKeyId, accessKeySecret); + // 读取租户的短信配置(SettingController写入的key格式为:sms:{tenantId}) + if (tenantId != null) { + String settingJson = redisUtil.get("sms:" + tenantId); + // 兼容历史key + if (StrUtil.isBlank(settingJson)) { + settingJson = redisUtil.get("setting:sms:" + tenantId); + } + if (StrUtil.isNotBlank(settingJson)) { + JSONObject jsonObject = JSONObject.parseObject(settingJson); + accessKeyId = StrUtil.blankToDefault(jsonObject.getString("accessKeyId"), accessKeyId); + accessKeySecret = StrUtil.blankToDefault(jsonObject.getString("accessKeySecret"), accessKeySecret); + signName = StrUtil.blankToDefault( + StrUtil.blankToDefault(jsonObject.getString("signName"), jsonObject.getString("sign")), + signName + ); + templateCode = StrUtil.blankToDefault( + StrUtil.blankToDefault(jsonObject.getString("templateCode"), jsonObject.getString("userTemplateId")), + templateCode + ); + regionId = StrUtil.blankToDefault(jsonObject.getString("regionId"), regionId); + } + } + + if (StrUtil.isBlank(accessKeyId) || StrUtil.isBlank(accessKeySecret) || StrUtil.isBlank(signName) || StrUtil.isBlank(templateCode)) { + return fail("短信服务未配置,请在系统设置中完善短信配置"); + } + + DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret); IAcsClient client = new DefaultAcsClient(profile); CommonRequest request = new CommonRequest(); request.setSysMethod(MethodType.POST); request.setSysDomain("dysmsapi.aliyuncs.com"); request.setSysVersion("2017-05-25"); request.setSysAction("SendSms"); - request.putQueryParameter("RegionId", "cn-hangzhou"); + request.putQueryParameter("RegionId", regionId); request.putQueryParameter("PhoneNumbers", param.getPhone()); - request.putQueryParameter("SignName", sign); - request.putQueryParameter("TemplateCode", userTemplateId); + request.putQueryParameter("SignName", signName); + request.putQueryParameter("TemplateCode", templateCode); // 生成短信验证码 - Random randObj = new Random(); - String code = Integer.toString(100000 + randObj.nextInt(900000)); - request.putQueryParameter("TemplateParam", "{\"code\":" + code + "}"); + String code = Integer.toString(ThreadLocalRandom.current().nextInt(100000, 1000000)); + JSONObject templateParam = new JSONObject(); + templateParam.put("code", code); + request.putQueryParameter("TemplateParam", templateParam.toJSONString()); try { CommonResponse response = client.getCommonResponse(request); String json = response.getData(); Gson g = new Gson(); HashMap result = g.fromJson(json, HashMap.class); - if ("OK".equals(result.get("Message"))) { - System.out.println("短信发送成功========================" + result); + if ("OK".equals(result.get("Message")) || "OK".equals(result.get("Code"))) { + log.info("短信发送成功 phone={}, result={}", DesensitizedUtil.mobilePhone(param.getPhone()), result); cacheClient.set(param.getPhone(), code, 5L, TimeUnit.MINUTES); String key = "code:" + param.getPhone(); redisUtil.set(key, code, 5L, TimeUnit.MINUTES); return success("发送成功", result.get("Message")); } else { + log.warn("短信发送失败 phone={}, result={}", DesensitizedUtil.mobilePhone(param.getPhone()), result); return fail("发送失败"); } } catch (ServerException e) { - e.printStackTrace(); + log.error("短信发送失败(ServerException) phone={}", DesensitizedUtil.mobilePhone(param.getPhone()), e); + return fail("发送失败"); } catch (ClientException e) { - e.printStackTrace(); + log.error( + "短信发送失败(ClientException) phone={}, errCode={}, errMsg={}, requestId={}", + DesensitizedUtil.mobilePhone(param.getPhone()), + e.getErrCode(), + e.getErrMsg(), + e.getRequestId(), + e + ); + if ("InvalidAccessKeyId.NotFound".equals(e.getErrCode()) || "SignatureDoesNotMatch".equals(e.getErrCode())) { + return fail("短信配置错误,请检查AccessKeyId/AccessKeySecret是否正确"); + } + return fail("发送失败"); } - return fail("发送失败"); } @OperationLog