Commit 2d29bf4e6f7486f1f2b4d359d6796d92e996ad54

Authored by YunaiV
1 parent a6854026

【功能优化】短信:华为云的实现优化,调整 sender 配置到 accessKey 中

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,25 +49,43 @@ public class HuaweiSmsClient extends AbstractSmsClient {
49 super(properties); 49 super(properties);
50 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); 50 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
51 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); 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 @Override 79 @Override
55 public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, 80 public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
56 List<KeyValue<String, Object>> templateParams) throws Throwable { 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 StringBuilder requestBody = new StringBuilder(); 82 StringBuilder requestBody = new StringBuilder();
65 - appendToBody(requestBody, "from=", sender); 83 + appendToBody(requestBody, "from=", getSender());
66 appendToBody(requestBody, "&to=", mobile); 84 appendToBody(requestBody, "&to=", mobile);
67 - appendToBody(requestBody, "&templateId=", templateId); 85 + appendToBody(requestBody, "&templateId=", apiTemplateId);
68 appendToBody(requestBody, "&templateParas=", JsonUtils.toJsonString( 86 appendToBody(requestBody, "&templateParas=", JsonUtils.toJsonString(
69 convertList(templateParams, kv -> String.valueOf(kv.getValue())))); 87 convertList(templateParams, kv -> String.valueOf(kv.getValue()))));
70 - appendToBody(requestBody, "&statusCallback=", statusCallBack); 88 + appendToBody(requestBody, "&statusCallback=", properties.getCallbackUrl());
71 appendToBody(requestBody, "&extend=", String.valueOf(sendLogId)); 89 appendToBody(requestBody, "&extend=", String.valueOf(sendLogId));
72 JSONObject response = request("/sms/batchSendSms/v1/", "POST", requestBody.toString()); 90 JSONObject response = request("/sms/batchSendSms/v1/", "POST", requestBody.toString());
73 91
@@ -107,7 +125,7 @@ public class HuaweiSmsClient extends AbstractSmsClient { @@ -107,7 +125,7 @@ public class HuaweiSmsClient extends AbstractSmsClient {
107 + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + sha256Hex(requestBody); 125 + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + sha256Hex(requestBody);
108 String stringToSign = "SDK-HMAC-SHA256" + "\n" + sdkDate + "\n" + sha256Hex(canonicalRequest); 126 String stringToSign = "SDK-HMAC-SHA256" + "\n" + sdkDate + "\n" + sha256Hex(canonicalRequest);
109 String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 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 + ", " + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature); 129 + ", " + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature);
112 130
113 // 2. 发起请求 131 // 2. 发起请求
@@ -131,11 +149,10 @@ public class HuaweiSmsClient extends AbstractSmsClient { @@ -131,11 +149,10 @@ public class HuaweiSmsClient extends AbstractSmsClient {
131 149
132 @Override 150 @Override
133 public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { 151 public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
134 - // 相比较阿里短信,华为短信发送的时候需要额外的参数“通道号”,考虑到不破坏原有的的结构,  
135 - // 所以将 sender 通道号,拼接到 apiTemplateId 字段中,格式为 "apiTemplateId 通道号"(空格为分隔符) 152 + // 华为短信模板查询和发送短信,是不同的两套 key 和 secret,与阿里、腾讯的区别较大,这里模板查询校验暂不实现
136 String[] strs = apiTemplateId.split(" "); 153 String[] strs = apiTemplateId.split(" ");
137 Assert.isTrue(strs.length == 2, "格式不正确,需要满足:apiTemplateId sender"); 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 .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(null); 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,7 +29,7 @@ import static org.mockito.Mockito.mockStatic;
29 public class HuaweiSmsClientTest extends BaseMockitoUnitTest { 29 public class HuaweiSmsClientTest extends BaseMockitoUnitTest {
30 30
31 private final SmsChannelProperties properties = new SmsChannelProperties() 31 private final SmsChannelProperties properties = new SmsChannelProperties()
32 - .setApiKey(randomString())// 随机一个 apiKey,避免构建报错 32 + .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错
33 .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 33 .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
34 .setSignature("芋道源码"); 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,15 +95,16 @@ public class SmsClientTests {
95 @Test 95 @Test
96 @Disabled 96 @Disabled
97 public void testHuaweiSmsClient_sendSms() throws Throwable { 97 public void testHuaweiSmsClient_sendSms() throws Throwable {
  98 + String sender = "x8824060312575";
98 SmsChannelProperties properties = new SmsChannelProperties() 99 SmsChannelProperties properties = new SmsChannelProperties()
99 - .setApiKey(System.getenv("SMS_HUAWEI_ACCESS_KEY")) 100 + .setApiKey(System.getenv("SMS_HUAWEI_ACCESS_KEY") + " " + sender)
100 .setApiSecret(System.getenv("SMS_HUAWEI_SECRET_KEY")) 101 .setApiSecret(System.getenv("SMS_HUAWEI_SECRET_KEY"))
101 .setSignature("runpu"); 102 .setSignature("runpu");
102 HuaweiSmsClient client = new HuaweiSmsClient(properties); 103 HuaweiSmsClient client = new HuaweiSmsClient(properties);
103 // 准备参数 104 // 准备参数
104 Long sendLogId = System.currentTimeMillis(); 105 Long sendLogId = System.currentTimeMillis();
105 String mobile = "17321315478"; 106 String mobile = "17321315478";
106 - String apiTemplateId = "3644cdab863546a3b718d488659a99ef x8824060312575"; 107 + String apiTemplateId = "3644cdab863546a3b718d488659a99ef";
107 List<KeyValue<String, Object>> templateParams = List.of(new KeyValue<>("code", "1024")); 108 List<KeyValue<String, Object>> templateParams = List.of(new KeyValue<>("code", "1024"));
108 // 调用 109 // 调用
109 SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); 110 SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams);