今天大Boss给小优优说,你给咱们的注册系统增加一下短信验证吧,然鹅小优优找了很多平台,像什么2xx、互亿xx了等等,后来一it好友告诉小优优,说用极光短信吧,毕竟人家就想做消息推送出身的,技术以及实时性肯定没的说。

小优优觉得说的对,毕竟人家是专业搞消息推送的。原来给大家分享过极光推送文章,那今天给大家分享一下极光短信和JAVA的整合。

首先我们进入极光官网 找到极光短信(如下图)

file

不过使用人家产品肯定需要先注册一下,原来我都是用的公司的账号,今天用自己个人账号给大家做分享吧。

1、注册以及找到开发需要的信息:

1.1、点击首页注册按钮:

file

1.2、然后输入邮箱、手机号等注册信息:

file

1.3注册完成之后激活该邮件。

file

点击邮件链接进行激活:

file

激活成功页面:

file

1.4、注册完成以后登录该系统:

file

1.5 、填写公司信息(没有公司可以瞎填一个)

file

1.6、进入开发者平台

file

1.7、创建一个应用

file

随便起一个应用名字为了方便我就叫他测试:

file

点击应用设置,点击短信设置

file

1.8、审核信息。

我们发现需要实名认证才能使用,我说发短信还需要实名认证???? 后来仔细想想,万一你发送涉黄涉政的,肯定要实名认证的。

认证分为企业认证和个人认证:

file

我们就选择个人认证:

file

需要审核信息,静静等待几分钟:

file

审核完毕领取极光短信福利:

file

需要开通极光认证才可以领取,如果只做测试,简介可以瞎写一下。

file

领取短信条数:

file

找到masterSecret和appKey配置:

file

1.9、申请一下签名:

file

啥是签名:前缀猿码优创就是签名。

file

填写签名也就是短信前缀,如果没有公司则添加小程序或者公众号截图即可。

file

审核通过后找到签名id备用:

file

1.10、申请模板:

file

添加模板,审核通过会有一个模板id

file

找到模板id备用:

file

2、以上把基本工作已经做完,

你现在手里应该有 appKey、masterSecret、多个模板id、签名id(如果缺少,请先完成以上步骤)

如果都有了现在可以进入开发模式。

2.1查看文档:

我们先看一下极光短信的API文档:https://docs.jiguang.cn/

这文档写的非常有条理:

file

不拍他们马屁了:短信文档地址:https://docs.jiguang.cn/jsms/guideline/JSMS_guide/

发送短信API文档:

file

2.2:点击Java服务端SDK 进入github极光官方开源地址:

file

2.3:整合SpringBoot项目:

2.3.1、添加pom文件:

file

我们发现他的版本是:LATEST_VERSION,我看了目前最高版本是:1.2.9 我们直接在pom文件里写成1.2.9

file

pom文件:里面包含了日志的jar包大家可以自行添加
 <!--整合极光短信-->
        <dependency>
            <groupId>cn.jpush.api</groupId>
            <artifactId>jsms-client</artifactId>
            <version>1.2.9</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>cn.jpush.api</groupId>
            <artifactId>jiguang-common</artifactId>
            <version>1.0.8</version>
        </dependency>
        <!-- For log4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

2.3.2、发短信代码:

file


package cn.cnbuilder.smallapp.utils;

import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jsms.api.SendSMSResult;
import cn.jsms.api.common.SMSClient;
import cn.jsms.api.common.model.SMSPayload;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


@Component //交给spring管理
public class JGSmsUtil {

    // masterSecret https://www.jiguang.cn/dev2/#/overview/appCardList 开发者服务--->应用设置--->应用信息--> Master Secret
    public String masterSecret = "xxxx";

    //appKey https://www.jiguang.cn/dev2/#/overview/appCardList 开发者服务--->应用设置--->应用信息--> Master Secret
    public String appKey = "xxxxx";

    //初始化发短信客户端
    private SMSClient smsClient = new SMSClient(masterSecret, appKey);

    /**
     * 发送模板短信-验证码 示例1
     *
     * @param phoneNumber
     * create 2019/12/26 by kingyifan
     */
    public void sendSMSCode(String phoneNumber, String code) {
        try {//构建发送短信
            SMSPayload payload = SMSPayload.newBuilder()
                    .setMobileNumber(phoneNumber) // 手机号码
                    .setTempId(1)            // 短信模板ID 需要自行申请 模板id为:1的则自带验证码模板id
                    .addTempPara("code", code)  // key模板参数value:参数值  您的手机验证码:{{code}},有效期5分钟,请勿泄露。如非本人操作,请忽略此短信。谢谢!
                    .setSignId(xxxx)// 签名id 需要自行申请审核。个人也可以申请
                    .build();

            //发送短信 会返回msg_id
            SendSMSResult res = smsClient.sendTemplateSMS(payload);
            //////////////////////////////////////////执行业务/////////////////////////////////////////////////////
            //指向保存短信发送记录业务逻辑 可以直接扔到MQ
            /**
             * 第一个参数极光返回的消息id
             * 第二个发送的手机号
             * 第三个发送内容
             * 第四个发送时间
             * 保存到DB
             */
            //insertSendSmsLog(res.getMessageId(),phoneNumber,code,0,System.currentTimeMillis()/1000);
            //////////////////////////////////////////执行业务/////////////////////////////////////////////////////

        } catch (APIConnectionException e) {
            e.printStackTrace();
        } catch (APIRequestException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送模板短信-取快递 示例2
     *
     * @param phoneNumber 手机号
     * @param name        名字
     * @param address     地址
     *                    <p>
     *                    create 2019/12/26 by kingyifan
     */
    public void sendSMSOther(String name, String address, String phoneNumber) {
        try {
            SMSPayload payload = SMSPayload.newBuilder()
                    .setMobileNumber(phoneNumber) // 手机号码
                    .setTempId(xxxx)            // 短信模板ID 需要自行申请
                    .addTempPara("name", name)  // key模板参数value:参数值  尊敬的{{name}},您的快递到{{address}},请速来取一下。
                    .addTempPara("address", address)  // key模板参数value:参数值  尊敬的{{name}},您的快递到{{address}},请速来取一下。
                    .setSignId(xxxx)// 签名id 需要自行申请审核。个人也可以申请
                    .build();

            //发送短信
            SendSMSResult res = smsClient.sendTemplateSMS(payload);
            //////////////////////////////////////////执行业务/////////////////////////////////////////////////////
            //指向保存短信发送记录业务逻辑 可以直接扔到MQ
            /**
             * 第一个参数极光返回的消息id
             * 第二个发送的手机号
             * 第三个发送内容
             * 第四个发送时间
             * 保存到DB
             */
            //insertSendSmsLog(res.getMessageId(),phoneNumber,code,0,System.currentTimeMillis()/1000);
            //////////////////////////////////////////执行业务/////////////////////////////////////////////////////
        } catch (APIConnectionException e) {
            e.printStackTrace();
        } catch (APIRequestException e) {
            e.printStackTrace();
        }
    }

    /**
     * SHA1加密
     *
     * @param strSrc 明文
     * @return 加密之后的密文
     */
    public static String encrypt(String strSrc) {
        MessageDigest md = null;
        String strDes = null;
        byte[] bt = strSrc.getBytes();
        try {
            md = MessageDigest.getInstance("SHA-1");// 将此换成SHA-1、SHA-512、SHA-384等参数
            md.update(bt);
            strDes = bytes2Hex(md.digest()); // to HexString
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
        return strDes;
    }

    /**
     * byte数组转换为16进制字符串
     *
     * @param bts 数据源
     * @return 16进制字符串
     */
    private static String bytes2Hex(byte[] bts) {
        String des = "";
        String tmp = null;
        for (int i = 0; i < bts.length; i++) {
            tmp = (Integer.toHexString(bts[i] & 0xFF));
            if (tmp.length() == 1) {
                des += "0";
            }
            des += tmp;
        }
        return des;
    }

    /**
     * 延签判断是否是极光回调
     *
     * @param signature
     * @param nonce
     * @param timestamp
     * @return create kingyifan by  on 2019.12.26
     */
    public Boolean checkSign(String signature, String nonce, String timestamp) {
        //加密进行比对
        String str = String.format("appKey=%s&appMasterSecret=%s&nonce=%s×tamp=%s",
                appKey, masterSecret, nonce, timestamp);
        String new_signature = encrypt(str);
        if (signature.equals(new_signature)) {
            return true;
        }
        return false;
    }
}

2.3.3、测试发短信接口:

package cn.cnbuilder.smallapp.controller;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import cn.cnbuilder.smallapp.config.WxMaConfiguration;
import cn.cnbuilder.smallapp.controller.base.BaseController;
import cn.cnbuilder.smallapp.utils.JGSmsUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

import static cn.cnbuilder.smallapp.controller.CallBackTest.encrypt;

/**
 * 极光短信发送测试以及回调
 * create By KingYiFan on 2019/12/26
 */
@Api(description = "极光短信发送测试以及回调")
@RestController
@RequestMapping("/jiguang")
public class JiGuangController {

    @Autowired //注入极光工具类
    private JGSmsUtil jgSmsUtil;

    /**
     * 发送极光短信验证码 --测试API
     * create By KingYiFan on 2019/12/26
     */
    @ApiOperation(value = "发送极光短信验证码", notes = "发送极光短信验证码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phoneNumber", value = "发送的手机号", dataType = "String", paramType = "query")})
    @GetMapping(value = "/sms/sendCode")
    public String sendCode(@RequestParam(value = "phoneNumber", required = true) String phoneNumber) {

        try {
            //发送验证码 也可以发送其他模板信息
            jgSmsUtil.sendSMSCode(phoneNumber, "123456");
            return "发送成功";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "发送失败";
    }

    /**
     * 发送极光短信快递 --测试API
     * create By KingYiFan on 2019/12/26
     */
    @ApiOperation(value = "发送极光短信快递测试", notes = "发送极光短信快递测试")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phoneNumber", value = "发送的手机号", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "address", value = "地址", dataType = "String", paramType = "query")
    })
    @GetMapping(value = "/sms/sendKDInfo")
    public String sendKDInfo(@RequestParam(value = "phoneNumber", required = true) String phoneNumber, @RequestParam(value = "address", required = true) String address, @RequestParam(value = "name", required = true) String name) {

        try {
            //发送验证码 也可以发送其他模板信息
            jgSmsUtil.sendSMSOther(name, address, phoneNumber);
            return "发送成功";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "发送失败";
    }
}

执行发短信接口:我们发现已经成功发送短信了:

file

我们看到手机上已经收到短信了。

file

2.3.6、短信 回调:

哈哈哈哈你以为就这样就完了吗?你知道发送成功了,但是你根本不知道用户收到短信了么?

file

继续给大家分享一下极光短信回调接口: 接口文档地址:https://docs.jiguang.cn/jsms/server/callback/#_2

第一步先配置回调地址:回调地址一定是公网可以访问的,为了方便可以用一个内网映射外网工具比如:Ngrok/花生壳

file

回调地址需要做校验

规则:极光将给回调 URL 发起一个 GET 请求并附带一个 8 位随机字符串的参数 echostr , 开发者需在 Response Body 里原样输出 echostr的值。

file

   /**
     * 发送结果回调API校验 --测试API
     * 极光将给回调 URL 发起一个 GET 请求并附带一个 8 位随机字符串的参数 echostr , 开发者需在 Response Body 里原样输出 echostr的值。
     * create 2019/12/26 by kingyifan
     */
    @ApiOperation(value = "发送结果回调验证接口", notes = "发送结果回调验证接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phoneNumber", value = "发送的手机号", dataType = "String", paramType = "query")})
    @GetMapping(value = "/sms/callBack")
    public String SmsCallBack(String echostr) {

        try {
            return echostr;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}

进行校验:

file

回调是post,校验是get请求 回调的时候是加密的。

file

 /**
     * 发送结果回调API --测试API
     * create By KingYiFan on 2019/12/26
     * {
     * "data":"{"msgId":"1652496","phone":"15822889320","receiveTime":1577342316504,"status":4001}",
     * "signature":"9bd88a6e110088a06faf3b2754eba341e34dd60f",
     * "type":"SMS_REPORT",
     * "nonce":-6573564950165804387,
     * "timestamp":1577342316407
     * }
     */
    @ApiOperation(value = "发送结果回调API", notes = "发送极光短信验证码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phoneNumber", value = "发送的手机号", dataType = "String", paramType = "query")})
    @PostMapping(value = "/sms/callBack")
    public String SmsCallBack(String data, String signature, String type, String nonce, String timestamp) {

        try {

            //验签查询是否是极光回调
            Boolean isJiGuang = jgSmsUtil.checkSign(signature, nonce, timestamp);
            if (!isJiGuang) {
                return "验签失败,请求有误!";
            }
            JSONObject jsonObject = JSONUtil.parseObj(data);

            String msgId = jsonObject.getStr("msgId");// 	API 调用的时候返回的 msg_id
            String phone = jsonObject.getStr("phone");//短信送达手机号
            String receiveTime = jsonObject.getStr("receiveTime");//短信送达时间
            //https://docs.jiguang.cn/jsms/server/callback/ 具体发送状态码
            String status = jsonObject.getStr("status");//发送状态返回码

             //修改db发送状态

            System.out.println(msgId);
            System.out.println(phone);
            System.out.println(receiveTime);
            System.out.println(status);
            System.out.println(type);

            return "success";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "fail";
    }

最后花了20分钟给大家画了一个流程图:

file

以上就是极光发送验证码短信和通知短信以及发送短信状态回调整合。