免 JAR 依赖纯 API 调用阿里云短信接口
Posted sp42a
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了免 JAR 依赖纯 API 调用阿里云短信接口相关的知识,希望对你有一定的参考价值。
缘起
发送短信一般依赖于各大厂商的 SDK,用起来也比较简单,例如阿里云的加入以下 Maven 依赖。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.4</version>
</dependency>
可见 2017 版本比较旧的,而且又加入一大堆其他三方的依赖,
我心想,发个短信也不容易啊,于是瞧瞧有没有纯 HTTP API 的,——其实一般是有的,直接调用吧,忘记 jar 包、忘记 Maven。——阿里云的文档写的挺好的,一个 Java main
函数就是完整的程序。
重构
此 main
函数一测就通过,不过比较粗糙,咱们重构之~~首先定义一个 bean AliyunSmsEntity
,描述用到的各个字段。
/**
* 阿里云短信实体
*/
public class AliyunSmsEntity {
private String accessKeyId;
private String accessSecret;
private String phoneNumbers;
private String signName;
private String templateCode;
private String templateParam;
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessSecret() {
return accessSecret;
}
public void setAccessSecret(String accessSecret) {
this.accessSecret = accessSecret;
}
public String getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(String phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
public String getSignName() {
return signName;
}
public void setSignName(String signName) {
this.signName = signName;
}
public String getTemplateParam() {
return templateParam;
}
public void setTemplateParam(String templateParam) {
this.templateParam = templateParam;
}
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
}
接着是组装参数去发送,比较复杂,我拆分了几个小函数,并较多地利用了 Spring 自带的工具函数,例如 URL 编码、Base64、UUID 等,最后发送 GET
请求采用了我自己封装的 HTTP
请求工具 NetUtil。
import com.ajaxjs.net.http.NetUtil;
import com.ajaxjs.util.StrUtil;
import com.ajaxjs.util.logger.LogHelper;
import com.ajaxjs.util.map.JsonHelper;
import org.springframework.util.AlternativeJdkIdGenerator;
import org.springframework.util.Base64Utils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 阿里云发送短信
*/
public class AliyunSMS {
private static final LogHelper LOGGER = LogHelper.getLog(AliyunSMS.class);
/**
* 请求的时间戳。按照ISO8601 标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。
*/
static String getTimestamp() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(new SimpleTimeZone(0, "GMT"));// 这里一定要设置GMT时区
return df.format(new Date());
}
/**
* 初始化请求参数
*
* @return
*/
static Map<String, String> initParams() {
Map<String, String> paras = new HashMap<>();
// 1. 系统参数
paras.put("SignatureMethod", "HMAC-SHA1");
paras.put("SignatureNonce", new AlternativeJdkIdGenerator().generateId().toString());
paras.put("SignatureVersion", "1.0");
paras.put("Timestamp", getTimestamp());
paras.put("Format", "JSON");
paras.put("Action", "SendSms");
paras.put("Version", "2017-05-25");
return paras;
}
/**
* 设置业务API参数
*
* @param paras
* @param entity
*/
static void setParams(Map<String, String> paras, AliyunSmsEntity entity) {
// 2. 业务API参数
paras.put("AccessKeyId", entity.getAccessKeyId());
paras.put("PhoneNumbers", entity.getPhoneNumbers());
paras.put("SignName", entity.getSignName());
paras.put("TemplateParam", entity.getTemplateParam());
paras.put("TemplateCode", entity.getTemplateCode());
// 3. 去除签名关键字Key
if (paras.containsKey("Signature"))
paras.remove("Signature");
}
/**
* 根据参数Key排序(顺序)
*
* @param paras
* @return
*/
static String sort(Map<String, String> paras) {
// 4. 参数KEY排序
TreeMap<String, String> sortParas = new TreeMap<>();
sortParas.putAll(paras);
// 5. 构造待签名的字符串
Iterator<String> it = sortParas.keySet().iterator();
StringBuilder sortQueryStringTmp = new StringBuilder();
while (it.hasNext()) {
String key = it.next();
sortQueryStringTmp.append("&").append(StrUtil.urlEncode(key)).append("=").append(StrUtil.urlEncode(paras.get(key)));
}
return sortQueryStringTmp.toString();
}
/**
* 构造待签名的请求串
*
* @param sortQueryStringTmp
* @param accessSecret
* @return
*/
static String makeSignature(String sortQueryStringTmp, String accessSecret) {
StringBuilder stringToSign = new StringBuilder();
stringToSign.append("GET").append("&");
stringToSign.append(StrUtil.urlEncode("/")).append("&");
stringToSign.append(StrUtil.urlEncode(sortQueryStringTmp.substring(1)));// 去除第一个多余的&符号
String sign = sign(accessSecret + "&", stringToSign.toString());
// 6. 签名最后也要做特殊URL编码
return StrUtil.urlEncode(sign);
}
/**
* 签名采用HmacSHA1算法 + Base64,编码采用UTF-8
*
* @param accessSecret
* @param stringToSign
* @return
*/
static String sign(String accessSecret, String stringToSign) {
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(accessSecret.getBytes("UTF-8"), "HmacSHA1"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
return Base64Utils.encodeToString(signData);
} catch (Exception e) {
LOGGER.warning(e);
return null;
}
}
private final static String SMS_API = "http://dysmsapi.aliyuncs.com/?Signature=";
/**
* @param entity
* @return true 表示发送成功
*/
public static String send(AliyunSmsEntity entity) {
Map<String, String> paras = initParams();
setParams(paras, entity);
String sortQueryStringTmp = sort(paras);
String signature = makeSignature(sortQueryStringTmp, entity.getAccessSecret());
// 最终打印出合法GET请求的URL
String url = SMS_API + signature + sortQueryStringTmp;
String json = NetUtil.get(url);
LOGGER.info("发送短信[{0}],结果:[{1}]", url, json);
Map<String, Object> map = JsonHelper.parseMap(json);
return "OK".equals(map.get("Code")) ? "OK" : map.get("Message").toString();
}
}
使用方法如下。
public static void main(String[] args) {
AliyunSmsEntity entity = new AliyunSmsEntity();
entity.setAccessKeyId("xxx");
entity.setAccessSecret("xxx");
entity.setSignName("测试");
entity.setTemplateCode("SMS_xxx");
entity.setTemplateParam("{\\"code\\":878799}");
entity.setPhoneNumbers("xxx");
System.out.println(send(entity));
}
测试通过!没那么多 jar 包,简单清爽!
以上是关于免 JAR 依赖纯 API 调用阿里云短信接口的主要内容,如果未能解决你的问题,请参考以下文章