Commit 2d29bf4e6f7486f1f2b4d359d6796d92e996ad54
1 parent
a6854026
【功能优化】短信:华为云的实现优化,调整 sender 配置到 accessKey 中
Showing
3 changed files
with
35 additions
and
17 deletions
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java
| ... | ... | @@ -49,25 +49,43 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 49 | 49 | super(properties); |
| 50 | 50 | Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); |
| 51 | 51 | Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); |
| 52 | + validateSender(properties); | |
| 53 | + } | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * 参数校验华为云的 sender 通道号 | |
| 57 | + * | |
| 58 | + * 原因是:验华为云发放短信的时候,需要额外的参数 sender | |
| 59 | + * | |
| 60 | + * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 | |
| 61 | + * | |
| 62 | + * @param properties 配置 | |
| 63 | + */ | |
| 64 | + private static void validateSender(SmsChannelProperties properties) { | |
| 65 | + String combineKey = properties.getApiKey(); | |
| 66 | + Assert.notEmpty(combineKey, "apiKey 不能为空"); | |
| 67 | + String[] keys = combineKey.trim().split(" "); | |
| 68 | + Assert.isTrue(keys.length == 2, "华为云短信 apiKey 配置格式错误,请配置 为[accessKeyId sender]"); | |
| 69 | + } | |
| 70 | + | |
| 71 | + private String getAccessKey() { | |
| 72 | + return StrUtil.subBefore(properties.getApiKey(), " ", true); | |
| 73 | + } | |
| 74 | + | |
| 75 | + private String getSender() { | |
| 76 | + return StrUtil.subAfter(properties.getApiKey(), " ", true); | |
| 52 | 77 | } |
| 53 | 78 | |
| 54 | 79 | @Override |
| 55 | 80 | public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, |
| 56 | 81 | List<KeyValue<String, Object>> templateParams) throws Throwable { |
| 57 | - // 1. 执行请求 | |
| 58 | - // 参考链接 https://support.huaweicloud.com/api-msgsms/sms_05_0001.html | |
| 59 | - // 相比较阿里短信,华为短信发送的时候需要额外的参数“通道号”,考虑到不破坏原有的的结构, | |
| 60 | - // 所以将 sender 通道号,拼接到 apiTemplateId 字段中,格式为 "apiTemplateId 通道号"(空格为分隔符) | |
| 61 | - String sender = apiTemplateId.split(" ")[1]; // 中国大陆短信签名通道号或全球短信通道号 | |
| 62 | - String templateId = apiTemplateId.split(" ")[0]; //模板ID | |
| 63 | - String statusCallBack = properties.getCallbackUrl(); | |
| 64 | 82 | StringBuilder requestBody = new StringBuilder(); |
| 65 | - appendToBody(requestBody, "from=", sender); | |
| 83 | + appendToBody(requestBody, "from=", getSender()); | |
| 66 | 84 | appendToBody(requestBody, "&to=", mobile); |
| 67 | - appendToBody(requestBody, "&templateId=", templateId); | |
| 85 | + appendToBody(requestBody, "&templateId=", apiTemplateId); | |
| 68 | 86 | appendToBody(requestBody, "&templateParas=", JsonUtils.toJsonString( |
| 69 | 87 | convertList(templateParams, kv -> String.valueOf(kv.getValue())))); |
| 70 | - appendToBody(requestBody, "&statusCallback=", statusCallBack); | |
| 88 | + appendToBody(requestBody, "&statusCallback=", properties.getCallbackUrl()); | |
| 71 | 89 | appendToBody(requestBody, "&extend=", String.valueOf(sendLogId)); |
| 72 | 90 | JSONObject response = request("/sms/batchSendSms/v1/", "POST", requestBody.toString()); |
| 73 | 91 | |
| ... | ... | @@ -107,7 +125,7 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 107 | 125 | + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + sha256Hex(requestBody); |
| 108 | 126 | String stringToSign = "SDK-HMAC-SHA256" + "\n" + sdkDate + "\n" + sha256Hex(canonicalRequest); |
| 109 | 127 | String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 |
| 110 | - headers.put("Authorization", "SDK-HMAC-SHA256" + " " + "Access=" + properties.getApiKey() | |
| 128 | + headers.put("Authorization", "SDK-HMAC-SHA256" + " " + "Access=" + getAccessKey() | |
| 111 | 129 | + ", " + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature); |
| 112 | 130 | |
| 113 | 131 | // 2. 发起请求 |
| ... | ... | @@ -131,11 +149,10 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 131 | 149 | |
| 132 | 150 | @Override |
| 133 | 151 | public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { |
| 134 | - // 相比较阿里短信,华为短信发送的时候需要额外的参数“通道号”,考虑到不破坏原有的的结构, | |
| 135 | - // 所以将 sender 通道号,拼接到 apiTemplateId 字段中,格式为 "apiTemplateId 通道号"(空格为分隔符) | |
| 152 | + // 华为短信模板查询和发送短信,是不同的两套 key 和 secret,与阿里、腾讯的区别较大,这里模板查询校验暂不实现 | |
| 136 | 153 | String[] strs = apiTemplateId.split(" "); |
| 137 | 154 | Assert.isTrue(strs.length == 2, "格式不正确,需要满足:apiTemplateId sender"); |
| 138 | - return new SmsTemplateRespDTO().setId(strs[0]).setContent(null) | |
| 155 | + return new SmsTemplateRespDTO().setId(apiTemplateId).setContent(null) | |
| 139 | 156 | .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(null); |
| 140 | 157 | } |
| 141 | 158 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java
| ... | ... | @@ -29,7 +29,7 @@ import static org.mockito.Mockito.mockStatic; |
| 29 | 29 | public class HuaweiSmsClientTest extends BaseMockitoUnitTest { |
| 30 | 30 | |
| 31 | 31 | private final SmsChannelProperties properties = new SmsChannelProperties() |
| 32 | - .setApiKey(randomString())// 随机一个 apiKey,避免构建报错 | |
| 32 | + .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 | |
| 33 | 33 | .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 |
| 34 | 34 | .setSignature("芋道源码"); |
| 35 | 35 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java
| ... | ... | @@ -95,15 +95,16 @@ public class SmsClientTests { |
| 95 | 95 | @Test |
| 96 | 96 | @Disabled |
| 97 | 97 | public void testHuaweiSmsClient_sendSms() throws Throwable { |
| 98 | + String sender = "x8824060312575"; | |
| 98 | 99 | SmsChannelProperties properties = new SmsChannelProperties() |
| 99 | - .setApiKey(System.getenv("SMS_HUAWEI_ACCESS_KEY")) | |
| 100 | + .setApiKey(System.getenv("SMS_HUAWEI_ACCESS_KEY") + " " + sender) | |
| 100 | 101 | .setApiSecret(System.getenv("SMS_HUAWEI_SECRET_KEY")) |
| 101 | 102 | .setSignature("runpu"); |
| 102 | 103 | HuaweiSmsClient client = new HuaweiSmsClient(properties); |
| 103 | 104 | // 准备参数 |
| 104 | 105 | Long sendLogId = System.currentTimeMillis(); |
| 105 | 106 | String mobile = "17321315478"; |
| 106 | - String apiTemplateId = "3644cdab863546a3b718d488659a99ef x8824060312575"; | |
| 107 | + String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; | |
| 107 | 108 | List<KeyValue<String, Object>> templateParams = List.of(new KeyValue<>("code", "1024")); |
| 108 | 109 | // 调用 |
| 109 | 110 | SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); | ... | ... |