SMSSendUtils.java
8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package com.zhongzhi.common.utils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import springfox.documentation.service.ApiKey;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/***
* 短信发送工具类
*/
@Slf4j
public class SMSSendUtils {
/**
* 短信平台接口配置中申请,替换为企业自己的API_KEY
*/
private static String API_KEY = "01b0312e7d016d2af41a47e5ba2c2748";
/**
* 短信平台接口配置中申请,替换为企业自己的SECRET_KEY
*/
private static String SECRET_KEY = "bd985a3772ca3b362f8b049a3017b6edac604815e34be96631513e29afbcac47";
private static final String DOMAIN = "https://opassapi.infocloud.cc/";
private static final String MESSAGE_SEND="message/send";
private static final String MESSAGE_SEND_BATCH = "message/sendBatch";
public static String SMS_API_KEY = "sms.api.key";
public static String SMS_SECRET_KEY = "sms.secret.key";
public static final int CONNECT_TIMEOUT= 30_000;
public static final int SOCKET_TIMEOUT = 15_000;
private static ReentrantLock lock = new ReentrantLock(true);
private static AtomicBoolean init = new AtomicBoolean(false);
static void init(){
// String apiKey = API_KEY;
// String secretKey = SECRET_KEY;
// System.setProperty(SMSSendUtils.SMS_API_KEY,API_KEY);
// System.setProperty(SMSSendUtils.SMS_SECRET_KEY,SECRET_KEY);
// if(StringUtils.isAnyEmpty(apiKey,secretKey)){
// throw new NullPointerException("短信发送apiKey、secretKey不能为空。\n解决方式:\n1.代码设置。" +
// "System.setProperty(SMSSendUtils.SMS_API_KEY,\"从短信平台接口配置中获取\");" +
// "System.setProperty(SMSSendUtils.SMS_SECRET_KEY,\"从短信平台接口配置中获取\");\n" +
// "2.启动配置。java -Dsms.api.key=xx -Dsms.secret.key=xxx");
// }
// API_KEY = apiKey;
// SECRET_KEY = secretKey;
}
private SMSSendUtils(){}
/**
* 短信发送。无参短信模板内容发送,即所有手机号接收到的短信内容一样
* @param templateCode
* @param phones 手机号。手机号最大个数限制1000
* @return
*/
public static HttpResponse send(String templateCode, String ...phones) throws IOException {
return send(templateCode,null,phones);
}
/**
* 短信发送。不同手机号对应相同的短信模板变量值,即所有手机号接收到的短信内容一样
* @param templateCode
* @param params 模板参数变量值
* @param phones 手机号。手机号最大个数限制1000
* @return
*/
public static HttpResponse send(String templateCode, LinkedList<String> params, String ... phones) throws IOException {
//手机号先去重,然后再转换
return sendBatch(templateCode,params!=null&&!params.isEmpty()?JSON.toJSONString(params):null,Arrays.stream(phones).collect(Collectors.toSet()).stream().collect(Collectors.joining(",")), false);
}
/**
* 短信发送
* @param templateCode 模板ID
* @param paramsJson 模板参数
* @param phonesJson 手机号
* @param batch true:使用MESSAGE_SEND_BATCH接口发送;false:使用MESSAGE_SEND接口发送
* @return HttpResponse
* @throws IOException
*/
private static HttpResponse sendBatch(String templateCode,String paramsJson,String phonesJson,boolean batch) throws IOException {
if(StringUtils.isAnyEmpty(templateCode)){
throw new NullPointerException("模板id或手机号不能为空");
}
Map<String, Object> headers = getHeaders();
Map<String, Object> bodyParams = new HashMap<>(4);
if(batch){
if(StringUtils.isAnyEmpty(paramsJson)){
throw new NullPointerException("短信发送所有手机号接收到的短信内容不一样时,模板参数值不能为空");
}
bodyParams.put("phonesJson", phonesJson);
//模板变量内容
bodyParams.put("templateParamJson", paramsJson);
}else{
bodyParams.put("phones", phonesJson);
//模板变量内容
if(StringUtils.isNotEmpty(paramsJson)) {
bodyParams.put("templateParam", paramsJson);
}
}
bodyParams.put("templateCode", templateCode);
//排序
// bd985a3772ca3b362f8b049a3017b6edac604815e34be96631513e29afbcac47
SortedMap<String, Object> sortedMap = new TreeMap<>(bodyParams);
headers.forEach((k, v) -> sortedMap.put(k, v));
//生成签名
headers.put("x-sign", getSignature(SECRET_KEY, sortedMap, HmacAlgorithms.HMAC_SHA_224));
Request request = Request.Post(DOMAIN+(batch?MESSAGE_SEND_BATCH:MESSAGE_SEND))
.version(HttpVersion.HTTP_1_1)
//设置连接超时
.connectTimeout(CONNECT_TIMEOUT)
//设置文本读取超时
.socketTimeout(SOCKET_TIMEOUT);
headers.forEach((k,v)->request.addHeader(k,String.valueOf(v)));
return request.bodyString(JSON.toJSONString(bodyParams), ContentType.APPLICATION_JSON)
.execute()
.returnResponse();
}
private static Map<String, Object> getHeaders(){
if(init.get()==false){
lock.lock();
try {
if(init.get()==false) {
init.set(true);
init();
}
}catch (Exception e){
throw e;
}finally {
lock.unlock();
}
}
Map<String, Object> headers = new HashMap<>();
headers.put("x-api-key", API_KEY);
headers.put("x-sign-method", HmacAlgorithms.HMAC_SHA_224.getName());
headers.put("x-nonce", getRandomNickname(10));
headers.put("x-timestamp", String.valueOf(System.currentTimeMillis()));
return headers;
}
/**
* 签名生成
* @param secret
* @param sortedMap
* @param hmacAlgorithms
* @return
*/
private static String getSignature(String secret, SortedMap<String, Object> sortedMap, HmacAlgorithms hmacAlgorithms) {
// 将参数拼接为字符串
// e.g. "key1=value1&key2=value2"
StringBuffer plainText = new StringBuffer();
for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
plainText.append(entry.getKey() + "=" + entry.getValue());
plainText.append("&");
}
if(StringUtils.isNotEmpty(plainText)) {
plainText.deleteCharAt(plainText.length() - 1);
}
log.info("\n加密方式:{} \n排序后的请求参数:{}",hmacAlgorithms.getName(),plainText);
return new HmacUtils(hmacAlgorithms, secret).hmacHex(plainText.toString());
}
/**
* 随机数生成
* @param length
* @return
*/
private static String getRandomNickname(int length) {
String val = "";
Random random = new Random();
for (int i = 0; i < length; i++) {
// 输出字母还是数字
String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
// 字符串
if ("char".equalsIgnoreCase(charOrNum)) {
// 取得大写字母还是小写字母
int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;
val += (char) (choice + random.nextInt(26));
} else if ("num".equalsIgnoreCase(charOrNum)) { // 数字
val += String.valueOf(random.nextInt(10));
}
}
return val;
}
}