官方文档
依赖
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
调用方式
以下为短信发送调用方式,其他接口调用方式类似
private String endPoint = "dysmsapi.aliyuncs.com";
private String ACCESS_KEY_ID;
private String ACCESS_SECRET;
private String SIGN_NAME;
private String ACTION;
private String sendSmsFromAliYun(String templateCode, HashMap<String, String> templateParamMap, String phoneNumbers) {
//获取timestamp(UTC时间)
ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
String formattedTime = currentTime.format(formatter);
//模板内字段转为json
String templateParam = JSON.toJSONString(templateParamMap);
//获取随机数
String random = String.valueOf(Math.random());
//包装请求参数
HashMap<String, String> params = new HashMap<>();
//公共参数
params.put("AccessKeyId", ACCESS_KEY_ID);
params.put("Action", ACTION);
params.put("Format", "JSON");
params.put("RegionId", "cn-beijing");
params.put("SignatureMethod", "HMAC-SHA1");
params.put("SignatureNonce", random);
params.put("SignatureVersion", "1.0");
params.put("Timestamp", formattedTime);
params.put("Version", "2017-05-25");
//接口参数
params.put("PhoneNumbers", phoneNumbers);
params.put("SignName", SIGN_NAME);
params.put("TemplateCode", templateCode);
params.put("TemplateParam", templateParam);
//生成阿里云签名
String canonicalizedQueryString = encodeURIComponent(sortParam(params));
//此接口采用POST方式,需要GET方法可自行更改(下方httpRequest也需要换成get方法)
String signature = generateSignature("POST", canonicalizedQueryString, ACCESS_SECRET);
//合成请求url
String finalUrl = generateFinalUrl(canonicalizedQueryString, signature);
//post
String result = HttpRequest.post(finalUrl).execute().body();
return result;
}
签名计算方法
//按字典序对参数排序
private HashMap<String, String> sortParam(HashMap<String, String> params) {
// 将HashMap的entrySet转换为List
List<Map.Entry<String, String>> entryList = new ArrayList<>(params.entrySet());
// 使用Collections.sort对List进行排序,传入自定义的Comparator
Collections.sort(entryList, new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
// 创建一个新的有序HashMap
HashMap<String, String> sortedParams = new LinkedHashMap<>();
// 将排序后的entry逐个放入sortedParams
for (Map.Entry<String, String> entry : entryList) {
sortedParams.put(entry.getKey(), entry.getValue());
}
// 返回排序后的HashMap
return sortedParams;
}
//按照RFC3986规则编码对请求参数和参数值进行编码
private String encodeURIComponent(HashMap<String, String> params) {
StringBuilder sb = new StringBuilder();
try {
// 遍历参数并进行编码
for (Map.Entry<String, String> entry : params.entrySet()) {
String encodedKey = encode(entry.getKey());
String encodedValue = encode(entry.getValue());
// 将编码后的参数和参数值用等号连接,并用与号连接多个参数
sb.append(encodedKey).append("=").append(encodedValue).append("&");
}
// 删除最后一个多余的与号
sb.delete(sb.length() - 1, sb.length());
} catch (Exception e) {
e.printStackTrace();
}
// 返回编码后的请求参数
return sb.toString();
}
//使用HMAC-SHA1算法计算签名
private String generateSignature(String httpMethod, String canonicalizedQueryString, String accessSecret) {
try {
//'/'URL编码后为%2F
String stringToSign = httpMethod + "&%2F&" + encode(canonicalizedQueryString);
accessSecret = accessSecret + "&";
// 将AccessSecret转换为UTF-8编码的字节数组
byte[] secretBytes = accessSecret.getBytes(StandardCharsets.UTF_8);
// 创建HMAC-SHA1算法实例,并使用AccessSecret初始化
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretBytes, "HmacSHA1");
mac.init(secretKeySpec);
// 计算待签名字符串的字节数组
byte[] stringToSignBytes = stringToSign.getBytes(StandardCharsets.UTF_8);
// 计算签名
byte[] signatureBytes = mac.doFinal(stringToSignBytes);
// 对签名进行Base64编码
String signature = Base64.getEncoder().encodeToString(signatureBytes);
return signature;
} catch (Exception e) {
return null;
}
}
//将生成的签名拼接至URL结尾
private String generateFinalUrl(String canonicalizedQueryString, String signature) {
return "http://" + endPoint + "/?" + canonicalizedQueryString + "&Signature=" + encode(signature);
}
//url编码 空格的编码为"%20"而不是"+"
private String encode(String s) {
try {
return URLEncoder.encode(s, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
} catch (Exception e) {
return null;
}
}