java对接短信平台

短信验证码目前是比较主流验证身份的一种方式,下面分享下我对接的几种短信平台

阿里云短信: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] &amp; 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("&amp;").append("sign").append("=").append(encode(sign));
        data.append("&amp;").append("sms_template_code").append("=").append(encode(smsParam.getSmsTemplateCode()));
        data.append("&amp;").append("sms_param").append("=").append(encode(smsParam.getContent()));
        data.append("&amp;").append("rec_num").append("=").append(encode(smsParam.getMobile()));
        data.append("&amp;").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)) &amp;&amp; (((Boolean) value).booleanValue() == true);
    }

    public boolean isFalse(Object key) {
        Object value = get(key);
        return ((value instanceof Boolean)) &amp;&amp; (!((Boolean) value).booleanValue());
    }

    @Override
    public boolean equals(Object ret) {
        return ((ret instanceof ResultUtil)) &amp;&amp; (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());
    }
}