新增华为云短信
This commit is contained in:
parent
0b580d2fe2
commit
29cfc30459
|
@ -20,18 +20,18 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信平台
|
* 短信平台
|
||||||
*
|
*
|
||||||
* @author 阿沐 babamu@126.com
|
* @author 阿沐 babamu@126.com
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("message/sms/platform")
|
@RequestMapping("message/sms/platform")
|
||||||
@Tag(name="短信平台")
|
@Tag(name = "短信平台")
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class SmsPlatformController {
|
public class SmsPlatformController {
|
||||||
private final SmsPlatformService smsPlatformService;
|
private final SmsPlatformService smsPlatformService;
|
||||||
|
@ -40,7 +40,7 @@ public class SmsPlatformController {
|
||||||
@GetMapping("page")
|
@GetMapping("page")
|
||||||
@Operation(summary = "分页")
|
@Operation(summary = "分页")
|
||||||
@PreAuthorize("hasAuthority('sms:platform:page')")
|
@PreAuthorize("hasAuthority('sms:platform:page')")
|
||||||
public Result<PageResult<SmsPlatformVO>> page(@Valid SmsPlatformQuery query){
|
public Result<PageResult<SmsPlatformVO>> page(@Valid SmsPlatformQuery query) {
|
||||||
PageResult<SmsPlatformVO> page = smsPlatformService.page(query);
|
PageResult<SmsPlatformVO> page = smsPlatformService.page(query);
|
||||||
|
|
||||||
return Result.ok(page);
|
return Result.ok(page);
|
||||||
|
@ -49,7 +49,7 @@ public class SmsPlatformController {
|
||||||
@GetMapping("{id}")
|
@GetMapping("{id}")
|
||||||
@Operation(summary = "信息")
|
@Operation(summary = "信息")
|
||||||
@PreAuthorize("hasAuthority('sms:platform:info')")
|
@PreAuthorize("hasAuthority('sms:platform:info')")
|
||||||
public Result<SmsPlatformVO> get(@PathVariable("id") Long id){
|
public Result<SmsPlatformVO> get(@PathVariable("id") Long id) {
|
||||||
SmsPlatformEntity entity = smsPlatformService.getById(id);
|
SmsPlatformEntity entity = smsPlatformService.getById(id);
|
||||||
|
|
||||||
return Result.ok(SmsPlatformConvert.INSTANCE.convert(entity));
|
return Result.ok(SmsPlatformConvert.INSTANCE.convert(entity));
|
||||||
|
@ -58,7 +58,7 @@ public class SmsPlatformController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "保存")
|
@Operation(summary = "保存")
|
||||||
@PreAuthorize("hasAuthority('sms:platform:save')")
|
@PreAuthorize("hasAuthority('sms:platform:save')")
|
||||||
public Result<String> save(@RequestBody SmsPlatformVO vo){
|
public Result<String> save(@RequestBody SmsPlatformVO vo) {
|
||||||
smsPlatformService.save(vo);
|
smsPlatformService.save(vo);
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
|
@ -67,13 +67,13 @@ public class SmsPlatformController {
|
||||||
@PostMapping("send")
|
@PostMapping("send")
|
||||||
@Operation(summary = "发送短信")
|
@Operation(summary = "发送短信")
|
||||||
@PreAuthorize("hasAuthority('sms:platform:update')")
|
@PreAuthorize("hasAuthority('sms:platform:update')")
|
||||||
public Result<String> send(@RequestBody SmsSendVO vo){
|
public Result<String> send(@RequestBody SmsSendVO vo) {
|
||||||
SmsPlatformEntity entity = smsPlatformService.getById(vo.getId());
|
SmsPlatformEntity entity = smsPlatformService.getById(vo.getId());
|
||||||
SmsConfig config = SmsPlatformConvert.INSTANCE.convert2(entity);
|
SmsConfig config = SmsPlatformConvert.INSTANCE.convert2(entity);
|
||||||
|
|
||||||
// 短信参数
|
// 短信参数
|
||||||
Map<String, String> params = new HashMap<>();
|
Map<String, String> params = new LinkedHashMap<>();
|
||||||
if(!StringUtils.isAnyBlank(vo.getParamKey(), vo.getParamValue())) {
|
if (StringUtils.isNotBlank(vo.getParamValue())) {
|
||||||
params.put(vo.getParamKey(), vo.getParamValue());
|
params.put(vo.getParamKey(), vo.getParamValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ public class SmsPlatformController {
|
||||||
smsService.saveLog(config, vo.getMobile(), params, null);
|
smsService.saveLog(config, vo.getMobile(), params, null);
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
}catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 保存日志
|
// 保存日志
|
||||||
smsService.saveLog(config, vo.getMobile(), params, e);
|
smsService.saveLog(config, vo.getMobile(), params, e);
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public class SmsPlatformController {
|
||||||
@PutMapping
|
@PutMapping
|
||||||
@Operation(summary = "修改")
|
@Operation(summary = "修改")
|
||||||
@PreAuthorize("hasAuthority('sms:platform:update')")
|
@PreAuthorize("hasAuthority('sms:platform:update')")
|
||||||
public Result<String> update(@RequestBody @Valid SmsPlatformVO vo){
|
public Result<String> update(@RequestBody @Valid SmsPlatformVO vo) {
|
||||||
smsPlatformService.update(vo);
|
smsPlatformService.update(vo);
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
|
@ -105,7 +105,7 @@ public class SmsPlatformController {
|
||||||
@DeleteMapping
|
@DeleteMapping
|
||||||
@Operation(summary = "删除")
|
@Operation(summary = "删除")
|
||||||
@PreAuthorize("hasAuthority('sms:platform:delete')")
|
@PreAuthorize("hasAuthority('sms:platform:delete')")
|
||||||
public Result<String> delete(@RequestBody List<Long> idList){
|
public Result<String> delete(@RequestBody List<Long> idList) {
|
||||||
smsPlatformService.delete(idList);
|
smsPlatformService.delete(idList);
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
package net.maku.message.sms;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import lombok.Data;
|
||||||
|
import net.maku.framework.common.exception.FastException;
|
||||||
|
import net.maku.framework.common.utils.JsonUtils;
|
||||||
|
import net.maku.message.sms.config.SmsConfig;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 华为云短信
|
||||||
|
*
|
||||||
|
* @author 阿沐 babamu@126.com
|
||||||
|
*/
|
||||||
|
public class HuaweiSmsStrategy implements SmsStrategy {
|
||||||
|
// 无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
|
||||||
|
private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
|
||||||
|
// 无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
|
||||||
|
private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
|
||||||
|
private final SmsConfig smsConfig;
|
||||||
|
|
||||||
|
public HuaweiSmsStrategy(SmsConfig smsConfig) {
|
||||||
|
this.smsConfig = smsConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(String mobile, Map<String, String> params) {
|
||||||
|
// APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
|
||||||
|
String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";
|
||||||
|
|
||||||
|
// 有参数则设置
|
||||||
|
String templateParas = null;
|
||||||
|
if (MapUtil.isNotEmpty(params)) {
|
||||||
|
templateParas = JsonUtils.toJsonString(params.values().toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求Body,不携带签名名称时,signature请填null
|
||||||
|
String body = buildRequestBody(smsConfig.getSenderId(), "+86" + mobile, smsConfig.getTemplateId(), templateParas, null, smsConfig.getSignName());
|
||||||
|
if (StringUtils.isBlank(body)) {
|
||||||
|
throw new FastException("body is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求Headers中的X-WSSE参数值
|
||||||
|
String wsseHeader = buildWsseHeader(smsConfig.getAccessKey(), smsConfig.getSecretKey());
|
||||||
|
if (StringUtils.isBlank(wsseHeader)) {
|
||||||
|
throw new FastException("wsse header is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 https
|
||||||
|
trustAllHttpsCertificates();
|
||||||
|
|
||||||
|
URL realUrl = new URL(url);
|
||||||
|
HttpsURLConnection connection = (HttpsURLConnection) realUrl.openConnection();
|
||||||
|
HostnameVerifier hv = (hostname, session) -> true;
|
||||||
|
connection.setHostnameVerifier(hv);
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.setUseCaches(true);
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
|
||||||
|
connection.setRequestProperty("X-WSSE", wsseHeader);
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
IoUtil.writeUtf8(connection.getOutputStream(), true, body);
|
||||||
|
|
||||||
|
int status = connection.getResponseCode();
|
||||||
|
if (status == HttpStatus.OK.value()) {
|
||||||
|
String response = IoUtil.read(connection.getInputStream(), CharsetUtil.CHARSET_UTF_8);
|
||||||
|
HuaweiSmsResult result = JsonUtils.parseObject(response, HuaweiSmsResult.class);
|
||||||
|
|
||||||
|
// 短信是否发送成功
|
||||||
|
assert result != null;
|
||||||
|
if (!"000000".equals(result.code)) {
|
||||||
|
throw new FastException(result.description);
|
||||||
|
}
|
||||||
|
} else { //400 401
|
||||||
|
throw new FastException(IoUtil.read(connection.getErrorStream(), CharsetUtil.CHARSET_UTF_8));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new FastException("短信发送失败:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造请求Body体
|
||||||
|
*
|
||||||
|
* @param signature | 签名名称,使用国内短信通用模板时填写
|
||||||
|
*/
|
||||||
|
static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
|
||||||
|
String statusCallBack, String signature) {
|
||||||
|
if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
|
||||||
|
|| templateId.isEmpty()) {
|
||||||
|
throw new FastException("buildRequestBody(): sender, receiver or templateId is null.");
|
||||||
|
}
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
|
||||||
|
map.put("from", sender);
|
||||||
|
map.put("to", receiver);
|
||||||
|
map.put("templateId", templateId);
|
||||||
|
if (null != templateParas && !templateParas.isEmpty()) {
|
||||||
|
map.put("templateParas", templateParas);
|
||||||
|
}
|
||||||
|
if (null != statusCallBack && !statusCallBack.isEmpty()) {
|
||||||
|
map.put("statusCallback", statusCallBack);
|
||||||
|
}
|
||||||
|
if (null != signature && !signature.isEmpty()) {
|
||||||
|
map.put("signature", signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String temp = "";
|
||||||
|
|
||||||
|
for (String s : map.keySet()) {
|
||||||
|
try {
|
||||||
|
temp = URLEncoder.encode(map.get(s), "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
sb.append(s).append("=").append(temp).append("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.deleteCharAt(sb.length() - 1).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造X-WSSE参数值
|
||||||
|
*/
|
||||||
|
static String buildWsseHeader(String appKey, String appSecret) {
|
||||||
|
if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
|
||||||
|
throw new FastException("buildWsseHeader(): appKey or appSecret is null.");
|
||||||
|
}
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
String time = sdf.format(new Date());
|
||||||
|
String nonce = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
|
||||||
|
MessageDigest md;
|
||||||
|
byte[] passwordDigest = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA-256");
|
||||||
|
md.update((nonce + time + appSecret).getBytes());
|
||||||
|
passwordDigest = md.digest();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest);
|
||||||
|
|
||||||
|
return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trustAllHttpsCertificates() throws Exception {
|
||||||
|
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||||
|
new X509TrustManager() {
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
SSLContext sc = SSLContext.getInstance("SSL");
|
||||||
|
sc.init(null, trustAllCerts, null);
|
||||||
|
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class HuaweiSmsResult {
|
||||||
|
// code为000000,表示成功
|
||||||
|
private String code;
|
||||||
|
private String description;
|
||||||
|
private List<Object> result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,15 @@ public class SmsContext {
|
||||||
private final SmsStrategy smsStrategy;
|
private final SmsStrategy smsStrategy;
|
||||||
|
|
||||||
public SmsContext(SmsConfig config) {
|
public SmsContext(SmsConfig config) {
|
||||||
if(config.getPlatform() == SmsPlatformEnum.ALIYUN.getValue()) {
|
if (config.getPlatform() == SmsPlatformEnum.ALIYUN.getValue()) {
|
||||||
this.smsStrategy = new AliyunSmsStrategy(config);
|
this.smsStrategy = new AliyunSmsStrategy(config);
|
||||||
}else if(config.getPlatform() == SmsPlatformEnum.QCLOUD.getValue()) {
|
} else if (config.getPlatform() == SmsPlatformEnum.QCLOUD.getValue()) {
|
||||||
this.smsStrategy = new QcloudSmsStrategy(config);
|
this.smsStrategy = new QcloudSmsStrategy(config);
|
||||||
}else {
|
} else if (config.getPlatform() == SmsPlatformEnum.QINIU.getValue()) {
|
||||||
|
this.smsStrategy = new QiniuSmsStrategy(config);
|
||||||
|
} else if (config.getPlatform() == SmsPlatformEnum.HUAWEI.getValue()) {
|
||||||
|
this.smsStrategy = new HuaweiSmsStrategy(config);
|
||||||
|
} else {
|
||||||
throw new FastException("未知的短信平台");
|
throw new FastException("未知的短信平台");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user