基于http使用feign封装阿里云短信
·
目前项目太多地方调用短信服务,微服务与微服务之间太多冗余代码,维护起来也不方便。项目中也会更改第三服务,由于多数时用spring-boot框架开发,这里封装一下短信服务,使用的时候只需要修改配置文件引入微服务即可。
SmsUtils.java
@Component
@Slf4j
public class SmsUtils {
@Autowired
private SmsFeignClient smsFeignClient;
@Value("${sms.signName}")
private String signName;
@Value("${sms.templateCode}")
private String templateCode;
@Value("${sms.accessKeyId}")
private String accessKeyId;
@Value("${sms.accessSecret}")
private String accessSecret;
private final static String CN_HANGZHOU = "cn-hangzhou";
private final static String SYS_VERSION = "2017-05-25";
private final static String SEND_SMS = "SendSms";
private final static String HMAC_SHA1 = "HMAC-SHA1";
private final static String SIGNATURE_VERSION = "1.0";
private final static String JSON = "json";
/**
* 发送短信
*
* @param phoneNumber,code
* @return
*/
public void sendSms(String phoneNumber, String code) throws Exception {
java.text.SimpleDateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
String timestamp = df.format(new Date());
String signatureNonce = UUID.randomUUID().toString();
HashMap<String, String> map = new HashMap<>(1);
map.put("code", code);
String signature = generateSignature(phoneNumber, signatureNonce, timestamp, code);
SmsSendRes res = smsFeignClient.sendSms(SEND_SMS, phoneNumber, signName, templateCode,
JSONUtil.toJsonStr(map), signature, accessKeyId, HMAC_SHA1, signatureNonce,
SIGNATURE_VERSION, timestamp, SYS_VERSION, JSON, CN_HANGZHOU);
if (null == res || !"OK".equals(res.getCode())) {
log.warn("发送短信异常:{}", JSONUtil.toJsonStr(res));
}
log.info("发送短信返回信息为:{}", JSONUtil.toJsonStr(res));
}
private String generateSignature(String phoneNumber, String signatureNonce, String timestamp, String code) throws Exception {
java.util.Map<String, String> paras = new java.util.HashMap<>();
// 1. 系统参数
paras.put("SignatureMethod", HMAC_SHA1);
paras.put("SignatureNonce", signatureNonce);
paras.put("AccessKeyId", accessKeyId);
paras.put("SignatureVersion", SIGNATURE_VERSION);
paras.put("Timestamp", timestamp);
paras.put("Format", JSON);
// 2. 业务API参数
paras.put("Action", SEND_SMS);
paras.put("Version", SYS_VERSION);
paras.put("RegionId", CN_HANGZHOU);
paras.put("PhoneNumbers", phoneNumber);
paras.put("SignName", signName);
paras.put("TemplateParam", "{\"code\":\"" + code + "\"}");
paras.put("TemplateCode", templateCode);
// 3. 去除签名关键字Key
paras.remove("Signature");
// 4. 参数KEY排序
java.util.TreeMap<String, String> sortParas = new java.util.TreeMap<>(paras);
// 5. 构造待签名的字符串
java.util.Iterator<String> it = sortParas.keySet().iterator();
StringBuilder sortQueryStringTmp = new StringBuilder();
while (it.hasNext()) {
String key = it.next();
sortQueryStringTmp.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
}
String sortedQueryString = sortQueryStringTmp.substring(1);// 去除第一个多余的&符号
String stringToSign = "GET" + "&" +
specialUrlEncode("/") + "&" +
specialUrlEncode(sortedQueryString);
return sign(accessSecret + "&", stringToSign);
}
private String specialUrlEncode(String value) throws Exception {
return java.net.URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
}
private String sign(String accessSecret, String stringToSign) throws Exception {
javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
mac.init(new javax.crypto.spec.SecretKeySpec(accessSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return new sun.misc.BASE64Encoder().encode(signData);
}
}
SmsFeignClient.java
@FeignClient(value = "sms-service",
fallback = SmsFeignClientFallBack.class,
url = "${sms-service.url}")
public interface SmsFeignClient {
@GetMapping
SmsSendRes sendSms(@RequestParam("Action") String action,
@RequestParam("PhoneNumbers") String phoneNumber,
@RequestParam("SignName") String signName,
@RequestParam("TemplateCode") String templateCode,
@RequestParam("TemplateParam") String templateParam,
@RequestParam("Signature") String signature,
@RequestParam("AccessKeyId") String accessKeyId,
@RequestParam("SignatureMethod") String signatureMethod,
@RequestParam("SignatureNonce") String signatureNonce,
@RequestParam("SignatureVersion") String signatureVersion,
@RequestParam("Timestamp") String timestamp,
@RequestParam("Version") String version,
@RequestParam("Format") String format,
@RequestParam("RegionId") String regionId);
}
SmsFeignClientFallBack.java
@Slf4j
@Component
public class SmsFeignClientFallBack implements FallbackFactory<SmsFeignClient> {
@Override
public SmsFeignClient create(Throwable throwable) {
return new SmsFeignClient() {
@Override
public SmsSendRes sendSms(String action,
String phoneNumber,
String signName,
String templateCode,
String templateParam,
String signature,
String accessKeyId,
String signatureMethod,
String signatureNonce,
String signatureVersion,
String timestamp,
String version,
String format,
String regionId) {
log.warn("发送短信异常:action->{}, phoneNumber->{}, signName->{}, " +
"templateCode->{}, templateParam->{}, signature->{}, " +
"accessKeyId->{}, signatureMethod->{}, signatureNonce->{}, " +
"signatureVersion->{}, timestamp->{}, version->{}" +
"format->{}, regionId->{},", action, phoneNumber, signName,
templateCode, templateParam, signature, accessKeyId,
signatureMethod, signatureNonce, signatureVersion, timestamp,
version, format, regionId);
return null;
}
};
}
}
下面介绍封装过程中遇到的错误问题:
Specified signature is not matched with our calculation. SignatureDoesNotMatch
报错意思是传过去的签名和阿里云根据传过去的数据所计算的签名不匹配,实际上是feign的参数必须与计算签名方法中得数据一致,博主之前在feign中有几个参数没有放,却在计算签名的时候加上去了,导致两边签名不一致。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)