短信验证码目前是比较主流验证身份的一种方式,下面分享下我对接的几种短信平台
阿里云短信:https://api.alidayu.com/docs/api.htm?spm=a3142.7395905.4.7.W5QHoB&apiId=26039
信信客短信:www.xinxinke.com(这个平台老板我熟,需要商业合作可以留言)
下面直接上代码
/**
* 短信发送工厂类
*
* @author zhangyi
* @version 2.0
* @time 2018年7月3日 下午3:31:57
*/
public class SmsFactory {
// snsSwitch 可以作为一个短信开关来使用,后面扩展其他平台时,可以在里面继续加,也可以通过反射的方式代替switch
public static SmsCore createSmsCore(int smsSwitch) {
SmsCore smsCore = null;
switch (smsSwitch) {
case 1:
smsCore = new AliYunSmsCore();
break;
case 2:
smsCore = new XxkSmsCore();
break;
} return smsCore;
}
}
/**
* 短信类
*
* @author zhangyi
* @version 2.0
* @time 2018年5月30日 下午2:02:01
*/
public abstract class SmsCore { public abstract ResultUtil send(SmsParam smsParam) throws Exception;
}
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile; public class AliYunSmsCore extends SmsCore { private static Log logger = LogFactory.getLog(AliYunSmsCore.class); // 产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
// 产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com"; public ResultUtil send(SmsParam smsParam) throws Exception { // 可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
// 初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", smsParam.getAppId(), smsParam.getAppKey());
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile); // 组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
// 必填:必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
request.setPhoneNumbers(smsParam.getMobile());
// 必填:短信签名-可在短信控制台中找到
request.setSignName(smsParam.getSignName());
// 必填:短信模板-可在短信控制台中找到
request.setTemplateCode(smsParam.getSmsTemplateCode());
// 可选:模板中的变量替换JSON串
request.setTemplateParam(smsParam.getContent()); logger.info("阿里云短信发送参数,手机号码:" + smsParam.getMobile() + ",验证码:" + smsParam.getContent() + ",短信签名:" + smsParam.getSignName() + ",短信模板:" + smsParam.getSmsTemplateCode()); // hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); logger.info("阿里云短信发送响应response,code:" + sendSmsResponse.getCode() + ", msg:" + sendSmsResponse.getMessage());
if ("OK".equalsIgnoreCase(sendSmsResponse.getCode())) {
return ResultUtil.ok().set("code", "200").set("msg", "发送成功!");
} else {
return ResultUtil.fail().set("code", "400").set("msg", sendSmsResponse.getMessage());
}
} }
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import net.sf.json.JSONObject; /**
* 信信客短信工具类
*/
public class XxkSmsCore extends SmsCore { private static Log logger = LogFactory.getLog(XxkSmsCore.class); // 转码
public String encode(String input) throws Exception {
return URLEncoder.encode(input, "UTF-8");
} // 计算 MD5 值
public static String md5(String input) throws Exception {
byte[] digest = MessageDigest.getInstance("MD5").digest(input.getBytes("UTF-8"));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < digest.length; i++) {
sb.append(String.format("%02x", Integer.valueOf(digest[i] & 0xFF)));
}
return sb.toString();
} // 发起 POST 请求
public String post(String url, String data) throws Exception { // 打开连接
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.connect(); // 输出参数
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(data);
dos.flush();
dos.close(); // 读取响应
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line = br.readLine();
br.close(); // 关闭连接
conn.disconnect(); return line;
} /**
* 短信发送接口
*
* @param smsTemplateCode
* 短信模板编号
* @param smsParam
* 短信模板变量 {\"code\":\"" + Math.random() + "\"}
* @param recNum
* 接收号码
* @param recNum
* 短信签名
* @throws Exception
*/
public ResultUtil send(SmsParam smsParam) throws Exception {
ResultUtil rs = ResultUtil.fail(); // 参数
String url = "http://www.xinxinke.com/api/send";
String sign = md5(smsParam.getAppId() + smsParam.getAppKey() + smsParam.getMobile()); // 拼接
StringBuffer data = new StringBuffer();
data.append("").append("dev_id").append("=").append(encode(smsParam.getAppId()));
data.append("&").append("sign").append("=").append(encode(sign));
data.append("&").append("sms_template_code").append("=").append(encode(smsParam.getSmsTemplateCode()));
data.append("&").append("sms_param").append("=").append(encode(smsParam.getContent()));
data.append("&").append("rec_num").append("=").append(encode(smsParam.getMobile()));
data.append("&").append("sms_sign").append("=").append(encode(smsParam.getSignName())); logger.info("信信客短信发送参数,手机号码:" + smsParam.getMobile() + ",验证码:" + smsParam.getContent() + ",短信签名:" + smsParam.getSignName() + ",短信模板:" + smsParam.getSmsTemplateCode()); String response = post(url, data.toString()); logger.info("信信客短信发送响应,response:" + response);
JSONObject jsonObject = JSONObject.fromObject(response);
String code = jsonObject.getString("code");
if ("25010".equals(code)) {
return ResultUtil.ok().set("code", "200").set("msg", "发送成功!");
} return rs.set("msg", jsonObject.getString("data"));
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// 使用了loombok插件,如果不使用则自己写set get方法即可
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SmsParam {
private String appId;
private String appKey; private String signName;
private String smsTemplateCode;
private String content;
private String mobile; public SmsParam(String appId, String appKey, String signName, String smsTemplateCode) {
this.appId = appId;
this.appKey = appKey;
this.signName = signName;
this.smsTemplateCode = smsTemplateCode;
}
}
其中 ResultUtil是返回工具类,如果不用,可以自行替换
import java.util.HashMap;
import java.util.Map; public class ResultUtil extends HashMap {
private static final String STATE = "state";
private static final String STATE_OK = "ok";
private static final String STATE_FAIL = "fail"; public static ResultUtil create(Object key, Object value) {
return new ResultUtil().set(key, value);
} public static ResultUtil create() {
return new ResultUtil();
} public static ResultUtil ok() {
return new ResultUtil().setOk();
} public static ResultUtil ok(Object key, Object value) {
return ok().set(key, value);
} public static ResultUtil fail() {
return new ResultUtil().setFail();
} public static ResultUtil fail(Object key, Object value) {
return fail().set(key, value);
} public ResultUtil setOk() {
super.put("state", "ok");
return this;
} public ResultUtil setFail() {
super.put("state", "fail");
return this;
} public boolean isOk() {
Object state = get("state");
if ("ok".equals(state)) {
return true;
}
if ("fail".equals(state)) {
return false;
}
throw new IllegalStateException("调用 isOk() 之前,必须先调用 ok()、fail() 或者 setOk()、setFail() 方法");
} public boolean isFail() {
Object state = get("state");
if ("fail".equals(state)) {
return true;
}
if ("ok".equals(state)) {
return false;
}
throw new IllegalStateException("调用 isFail() 之前,必须先调用 ok()、fail() 或者 setOk()、setFail() 方法");
} public ResultUtil set(Object key, Object value) {
super.put(key, value);
return this;
} public ResultUtil set(Map map) {
super.putAll(map);
return this;
} public ResultUtil set(ResultUtil ret) {
super.putAll(ret);
return this;
} public ResultUtil delete(Object key) {
super.remove(key);
return this;
} public <T> T getAs(Object key) {
return (T) get(key);
} public String getStr(Object key) {
Object s = get(key);
return s != null ? s.toString() : null;
} public Integer getInt(Object key) {
Number n = (Number) get(key);
return n != null ? Integer.valueOf(n.intValue()) : null;
} public Long getLong(Object key) {
Number n = (Number) get(key);
return n != null ? Long.valueOf(n.longValue()) : null;
} public Number getNumber(Object key) {
return (Number) get(key);
} public Boolean getBoolean(Object key) {
return (Boolean) get(key);
} public boolean notNull(Object key) {
return get(key) != null;
} public boolean isNull(Object key) {
return get(key) == null;
} public boolean isTrue(Object key) {
Object value = get(key);
return ((value instanceof Boolean)) && (((Boolean) value).booleanValue() == true);
} public boolean isFalse(Object key) {
Object value = get(key);
return ((value instanceof Boolean)) && (!((Boolean) value).booleanValue());
} @Override
public boolean equals(Object ret) {
return ((ret instanceof ResultUtil)) && (super.equals(ret));
} @Override
public String toString() {
return "ResultUtil [toString()=" + super.toString() + "]";
} }
实际调用
String content = String.valueOf(new Random().nextDouble()).substring(3, 9);
SmsParam smsParam = initSmsParam(templateType, smsSwitch);
smsParam.setMobile(mobile);
smsParam.setContent("{\"code\": \"" + content + "\"}"); // 工厂类生产具体短信发送类
SmsCore smsCore = SmsFactory.createSmsCore(smsSwitch); // 短信发送
resultUtil = smsCore.send(smsParam);
只用配置smsSwitch(短信服务选型:0=测试短信888888,1=阿里云短信,2=信信客短信),顺便贴出我实际用的配置
public enum SmsCoreTypeEnum {
TEST(0, "test"), AlI(1, "ali"), XXK(2, "xxk"); public int type;
public String desc; public int getType() {
return type;
} public String getDesc() {
return desc;
} private SmsCoreTypeEnum(int type, String desc) {
this.type = type;
this.desc = desc;
} public static void main(String[] args) {
System.out.println(AlI.getType() + "--" + AlI.getDesc());
System.out.println(SmsCoreTypeEnum.AlI.getType());
}
}