调用大汉三通短信接口
最近在做发送短信接口,公司选择了大汉三通短信平台,这里记录一下接口的调用步骤。
一、获取大汉三通的账号、密码请求参数
这个到大汉三通官网或者找公司相关人员要
二、查看大汉三通短信云通信接口手册
手册地址:
http://help.dahantc.com/docs/oss/1apkb302nt0tv.html
1、查询接口参数,这里以短信下发接口为例
2、申请短信模板
注意:
短信内容模板可以到大汉三通的后台申请,通过之后就可以使用了。
后台登录地址:
https://3tong.net/
这里的用户名和密码和调用接口的用户名和密码是同一个东西。
3、大汉三通jar包地址(非必要)
大汉三通是有自己jar包的,使用他们自己的jar包可以调用接口时密码不需要md5加密(目前就发现这个好处),但是大汉三通没有对应的maven依赖,需要把jar包变成本地依赖包再在maven工程中引入,这个过程比较繁琐,如果直接把jar包放到项目中引入,在项目打包后可能找不到jar包里面的工具类。
不使用大汉三通的jar包的情况下调用大汉三通的短信接口没有那么多弯,接口的必填参数拿到之后就可以调用接口了。使用了大汉三通的jar也不过调用接口时不要md5加密,这里就没有使用他们的jar包了。
jar包在这里
点击跳转到示例地址
在示例的lib目录下
之后将jar包导入到本地maven即可,导入步骤在这里。
三、示例代码
import cn.hutool.core.util.RandomUtil;
import org.jeecg.modules.system.util.MD5Util;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
/**
* 大汉三通短信工具类
*
* @author:user
* @date: 2022-08-16 17:53
*/
public class DahanUtil {
private static String account = "你的大汉三通用户名"; // 用户名(必填)
private static String password = "你的大汉三通密码"; // 密码(必填)
private static String phone = "19900000000"; // 手机号码(必填,多条以英文逗号隔开,国际短信要+国别号)
private static String content = ""; // 短信内容(必填)
public static String sign = "【大汉三通】"; // 短信签名(必填,格式如【大汉三通】)
public static String subcode = ""; // 子号码(选填)
public static String msgid = UUID.randomUUID().toString().replace("-", ""); // 短信id,(可选,不传我们生成一个返回)
public static String sendtime = ""; // 定时发送时间(可选)
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
String encryptPassword = MD5Util.encrypt(password);
String _url = "http://www.dh3t.com/json/sms/Submit";
String mesCode = RandomUtil.randomNumbers(4);
//这是来自后台的短信模板,不能随便写,定义在模板中的验证码等变量类似${0,5}这种形式
String info = "测试费用发放计划,金额0.01验证码为" + mesCode + "请在测试平台中输入验证码进行费用发放审核。"; // 短信内容
//请求参数
String requestParam = "{\"account\":\"" + account + "\",\"password\":\"" + encryptPassword + "\",\"msgid\":\"" + msgid + "\",\"phones\":\"" + phone + "\",\"content\":\"" + info + "\",\"sign\":\"" + sign + "\",\"subcode\":\"" + subcode + "\"}";
URL url = new URL(_url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(requestParam.getBytes("UTF-8"));
outputStream.flush();
outputStream.close();
//响应结果
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
StringBuffer stringBuffer = new StringBuffer();
int ch;
while ((ch = bufferedReader.read()) > -1) {
stringBuffer.append((char) ch);
}
//返回结果,类似这种{"msgid":"","result":"0","desc":"请求成功","failPhones":""}
System.out.println(stringBuffer);
bufferedReader.close();
Long end = System.currentTimeMillis();
System.out.println("发送短信耗时:" + (end - start));
}
}
测试结果:
{“msgid”:“”,“result”:“0”,“desc”:“请求成功”,“failPhones”:“”}
因为我这里没有白名单所以会报“ip鉴权失败”,就没有截图,如果报这个错就表示其他都没有问题了,就差白名单了。如果返回请求成功的话,那对应的手机号码过一会就可以收到验证码了。
四、工具类
这是我在项目中使用的工具类。
1、请求参数dto
import lombok.Data;
/**
* 大汉三通配置dto
*
* @author:user
* @date: 2022-08-16 18:14
*/
@Data
public class DahanConfig {
//设置默认值是为了让发送请求时为空的参数值不为“null”
// 用户名(必填)
private String account = "";
// 密码(必填)
private String password = "";
// 手机号码(必填,多条以英文逗号隔开,国际短信要+国别号)
private String phone = "";
// 短信内容(必填)
private String content = "";
// 短信签名(必填,格式如【大汉三通】)
private String sign = "";
// 大汉三通请求地址
private String url = "";
// 子号码(选填)
private String subcode = "";
// 短信id,(可选,不传我们生成一个返回)
private String msgid = "";
// 定时发送时间(可选)
private String sendtime = "";
}
2、替换短信内容的dto
import lombok.Data;
/**
* 替换短信内容dto
*
* @author:user
* @date: 2022-08-17 18:28
*/
@Data
public class ReplaceInfoDto {
//索引,用来和传入参数对应
private Integer index;
//被替换字符在原始字符串中的开始索引
private Integer start;
//被替换字符在原始字符串中的结束索引
private Integer end;
//在原始字符串中被替换字符
private String waitToReplaceStr;
//将替换原始字符串中被替换字符的参数
private Object replaceStr;
}
3、响应状态码枚举类
/**
* 大汉三通响应状态码
*
* @author:user
* @date: 2022-08-16 18:09
*/
public enum DahanSmsCodeEnum {
SUBMITTED_SUCCESSFULLY("0", "提交成功"),
INCORRECT_USERNAME_OR_PASSWORD("1", "账号或密码错误"),
MSGID_TOO_LONG("3", "msgid 太长,不得超过 64 位"),
WRONG_NUMBER("4", "错误号码 / 限制运营商号码"),
PHONE_NUMBER_LIMIT("5", "手机号码个数超过最大限制"),
SMS_CONTENT_LIMIT("6", "短信内容超过最大限制"),
INVALID_EXTENSION_SUBNUMBER("7", "扩展子号码无效"),
TIMING_TIME_FORMAT_ERROR("8", "定时时间格式错误"),
PHONE_EMPTY("14", "手机号码为空"),
USER_BANNED_OR_DISABLED("19", "用户被禁发或禁用"),
IP_AUTHENTICATION_FAILED("20", "ip 鉴权失败"),
SMS_CONTENT_EMPTY("21", "短信内容为空"),
NO_NUMBER_AVAILABLE("24", "无可用号码"),
BATCH_SMS_NUMBER_LIMIT("25", "批量提交短信数超过最大限制"),
SYSTEM_BUSY("98", "系统正忙"),
MALFORMED_MESSAGE("99", "消息格式错误");
private String code;
private String desc;
DahanSmsCodeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
4、MD5的加密工具类
可以使用自己的MD5加密工具类,使用了大汉三通jar包不需要MD5加密。
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5加密类(封装jdk自带的md5加密方法)
* 32位小写
*/
public class MD5Util {
public static String encrypt(String source) {
return encodeMd5(source.getBytes());
}
private static String encodeMd5(byte[] source) {
try {
return encodeHex(MessageDigest.getInstance("MD5").digest(source));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
private static String encodeHex(byte[] bytes) {
StringBuffer buffer = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
if (((int) bytes[i] & 0xff) < 0x10) {
buffer.append("0");
}
buffer.append(Long.toString((int) bytes[i] & 0xff, 16));
}
return buffer.toString();
}
}
5、调用接口的工具类
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.VerifyUtil;
import org.jeecg.modules.dahan.dto.DahanConfig;
import org.jeecg.modules.dahan.dto.ReplaceInfoDto;
import org.jeecg.modules.dahan.enums.DahanSmsCodeEnum;
import org.jeecg.modules.system.util.MD5Util;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 大汉三通短信工具类
*
* @author:user
* @date: 2022-08-16 17:53
*/
@Slf4j
public class DahanUtil {
private static String REGULAR = "\\$\\{\\d+\\,\\d+\\}"; //大汉三通模板中的匹配规则:\${\d+,\d+},匹配例如:${0,4}
private static Pattern PATTERN = Pattern.compile(REGULAR);
private static String account = "你的大汉三通账号"; // 用户名(必填)
private static String password = "你的大汉三通密码"; // 密码(必填)
private static String phone = "19000000000"; // 手机号码(必填,多条以英文逗号隔开,国际短信要+国别号)
private static String content = ""; // 短信内容(必填)
public static String sign = "【大汉三通】"; // 短信签名(必填,格式如【大汉三通】)
public static String subcode = ""; // 子号码(选填)
public static String msgid = UUID.randomUUID().toString().replace("-", ""); // 短信id,(可选,不传我们生成一个返回)
public static String sendtime = ""; // 定时发送时间(可选)
public static void main(String[] args) throws Exception {
String originalInfo = "${4,20}费用发放计划,金额${3,20}验证码为${0,5}请在测试平台中输入验证码进行费用发放审核。";
//使用参数替换短信中的字符(形如:${4,20}),按顺序替换
String content = getTargetStr(originalInfo, "测试", "0.01", "1122");
System.out.println(content);
DahanConfig config = new DahanConfig();
config.setAccount(account);
config.setPassword(password);
config.setPhone(phone);
config.setContent(content);
config.setSign(sign);
config.setUrl("http://www.dh3t.com/json/sms/Submit");
sendSms(config);
}
/**
* 获取替换参数替换了待替换字符串的字符串值
* @param source 原始字符串
* @param targetValues 替换参数,要按照替换字符的顺序传入
* @return
*/
public static String getTargetStr(String source, Object ...targetValues) {
List<ReplaceInfoDto> replaceInfoList = getReplaceInfoList(source, targetValues);
StringBuilder sourceStrBuilder = new StringBuilder(source);
int start;
int end;
int indexDifference = 0; //被替换的字符和将替换的字符的索引长度差,用来重新设置开始字符的索引
String currentWaitToReplaceStr = null; //当前待替换字符
String currentTargetStr = null; //当前目标字符
ReplaceInfoDto replaceInfo = null;
for (int i = 0; i < replaceInfoList.size(); i++) {
replaceInfo = replaceInfoList.get(i);
start = replaceInfo.getStart();
end = replaceInfo.getEnd();
currentWaitToReplaceStr = replaceInfo.getWaitToReplaceStr();
currentTargetStr = replaceInfo.getReplaceStr().toString();
if (i != 0) { //第一个待替换的字符不需要加上索引差
start += indexDifference;
end += indexDifference;
}
indexDifference += currentTargetStr.length() - currentWaitToReplaceStr.length(); //后面开始索引有偏差的话,索引长度差需要累加
sourceStrBuilder.replace(start, end, currentTargetStr); //从当前开始索引和结束索引,以目标参数替换待替换字符串
}
return sourceStrBuilder.toString();
}
/**
* 取出字符串中的符合正则表达式的所有字符串map,value为这个字符串的开始索引
* @param source 字符串
* @return
*/
public static Map<Integer, Integer> getTargetIndexMap(String source) {
VerifyUtil.checkParam(source, "被替换字符串不能为空!"); //这是校验空字符串的方法,之前的博客中有,也可以用自己的方式校验
Matcher matcher = PATTERN.matcher(source);
Map<Integer, Integer> result = new HashMap<>();//value为目标字符串中的开始索引
Integer index = 0; //map中的索引
while (matcher.find()) { //匹配成功的结果是按字符串中从左至右的顺序,使用索引方便后面取出
//当前目标字符串在原始字符串中的开始索引
result.put(index, matcher.start());
index++;
}
return result;
}
/**
* 取出字符串中的符合正则表达式的所有字符串,将它们的开始索引,结束索引,字符串值以及对应替换值设置到替换内容dto中
* @param source 原始字符串
* @param targetValues 替换数组
* @return
*/
public static List<ReplaceInfoDto> getReplaceInfoList(String source, Object ...targetValues) {
VerifyUtil.checkParam(source, "被替换字符串不能为空!");
if (getTargetIndexMap(source).size() != targetValues.length)
throw new RuntimeException("传入替换值数量与字符串中待替换字符数量不一致!");
Matcher matcher = PATTERN.matcher(source);
//因为每遍历一次,对象中的值都会被重新设置,所以只要new一个就可以了
ReplaceInfoDto replaceInfoDto = null;
//返回的替换内容集合
List<ReplaceInfoDto> replaceInfoDtoList = new ArrayList<>();
Integer index = 0; //索引,后面要从数组中取数据,索引从0开始
while (matcher.find()) { //匹配成功的结果是按字符串中从左至右的顺序,使用索引方便后面取出
replaceInfoDto = new ReplaceInfoDto();
replaceInfoDto.setIndex(index);
replaceInfoDto.setStart(matcher.start());
replaceInfoDto.setEnd(matcher.end());
replaceInfoDto.setWaitToReplaceStr(matcher.group());
replaceInfoDto.setReplaceStr(targetValues[index]); //传入替换参数与字符串中待替换的参数一致则可以用索引对应上
replaceInfoDtoList.add(replaceInfoDto);
index++;
}
return replaceInfoDtoList;
}
/**
* 发送大汉三通短信配置
* @param config
* @throws Exception
*/
public static void sendSms(DahanConfig config) throws Exception {
List<String> emptyFiledList = Arrays.asList("subcode", "msgid", "sendtime");//可以以自己的方式校验必填字段,这三个是可以为空的字段
VerifyUtil.checkBean(config, emptyFiledList);
long start = System.currentTimeMillis();
//没有使用大汉三通的jar包,需要进行md5加密
config.setPassword(MD5Util.encrypt(config.getPassword()));
String requestParam = "{\"account\":\"" + config.getAccount() + "\",\"password\":\"" + config.getPassword() + "\",\"msgid\":\"" + config.getMsgid() + "\",\"phones\":\"" + config.getPhone() + "\",\"content\":\"" + config.getContent() + "\",\"sign\":\"" + config.getSign() + "\",\"subcode\":\"" + config.getSubcode() + "\"}";
log.info("大汉三通请求参数:{}", requestParam);
//我这里是想用HttpURLConnection发送请求,可以用自己的方式发送请求
HttpURLConnection urlConnection = null;
OutputStreamWriter outputStream = null;
try {
//创建连接
URL url = new URL(config.getUrl());
urlConnection = (HttpURLConnection) url.openConnection();
//设置请求参数
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json");// 发送请求参数类型
urlConnection.setDoInput(true); //是否输入参数
urlConnection.setDoOutput(true); //是否输出参数
//获取写数据流
outputStream = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
outputStream.write(requestParam); //写数据,这里就是请求的返回结果
log.info("发送大汉三通短信:{}", outputStream);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
log.error("大汉三通请求参数信息错误:{}", e.getMessage());
throw new RuntimeException(e.getMessage());
} finally {
if (null != outputStream) {
outputStream.close();
}
}
log.info("大汉三通请求响应状态码:{}", urlConnection.getResponseCode());
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
StringBuilder stringBuilder = new StringBuilder();
try {
//获取响应参数
inputStream = urlConnection.getInputStream();
inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
int ch;
while ((ch = bufferedReader.read()) > -1) {
stringBuilder.append((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
log.error("获取响应体失败: {}", e.getMessage());
throw new RuntimeException(e.getMessage());
} finally {
if (null != inputStream) {
inputStream.close();
}
if (null != inputStreamReader) {
inputStreamReader.close();
}
if (null != bufferedReader) {
bufferedReader.close();
}
if (null != urlConnection) {
urlConnection.disconnect();
}
}
log.info("大汉三通响应信息:{}", stringBuilder);
JSONObject responseJson = JSONObject.parseObject(stringBuilder.toString());
//短信发送失败是返回错误信息
if (!DahanSmsCodeEnum.SUBMITTED_SUCCESSFULLY.getCode().equals(responseJson.getString("result")))
throw new RuntimeException(responseJson.getString("desc"));
long end = System.currentTimeMillis();
log.info("发送大汉三通短信耗时:{}", end - start);
}
}