zoukankan      html  css  js  c++  java
  • sms短信网关对接

    因为工作的需求,需要对接短信网关,业务上就是一个注册用户时,需要发送手机验证码;可能别的公司都是使用第三方接口,但是大点的公司,为了安全,他们都有自己的短信消息中心(SMSC)

     1.业务需求

        - 1.对接短信网关,发送消息,下行发送(MT)   使用openSMPP开源的开发包

        - 2.验证码使用redis存储(主要原因是,分布式存储,支持过期时间)

     2. 下面是简单的代码实例(包括发送短信和redis存储验证码)

     pom中添加依赖

       <dependency>
                <groupId>org.opensmpp</groupId>
                <artifactId>opensmpp-core</artifactId>
                <version>3.0.2</version>
            </dependency>
    public class SmsController {    
    
       @Autowiredprivate RedisTemplate redisTemplate;
    
    
        public boolean sendVerifyCode() {
            String destination = "8613594312686";
            String host = "192.168.XX";
            String port = "12345";
            String userName = "test";
            String password = "test";
            //存储redis中的key
            String key = getDestinationNumKey(destination);
            //redis中已存在验证码,则使用redis中存储的验证码(在15分钟的过期时间内发送同一条验证码),否则生成6位数字验证码
            Object valueByKey = getValueByKey(key);
            String verificationCode = Objects.nonNull(valueByKey) ? valueByKey.toString() : String.valueOf((int) (Math.random() * 900000 + 100000));
            //租户(不传,默认default)
            boolean sendSuccess = sendMessage(host, port, userName, password, destination, verificationCode);
            //发送成功后,验证码存入redis中,并设置过期时间(默认15分钟)
            if (sendSuccess) {
                putValueToRedisWithTimeout(key, verificationCode, 15, TimeUnit.MINUTES);
            }
            return sendSuccess;
        }
    
        private String getDestinationNumKey(String destination) {
            return String.format("%s:%s", "DESTINATION", destination);
        }
    
    
        public boolean sendMessage(String host,
                                   String port,
                                   String userName,
                                   String password,
                                   String phonenumber,
                                   String verifyCode) {
            log.info("start to send sms notification, reciever,host {},port {}, userName {} password {} destinations is {} verifyCode {}", host, port, userName, password, phonenumber, verifyCode);
            try {
                TCPIPConnection connection = new TCPIPConnection(host, Integer.parseInt(port));
                Session session = new Session(connection);
                BindRequest request = new BindTransmitter();
                request.setSystemId(userName);
                request.setPassword(password);
                //SMPP protocol version
                request.setInterfaceVersion((byte) 0x34);
                request.setSystemType("SMPP");
                BindResponse bind = session.bind(request);
                log.info("bind response debugString {},response command status {}", bind.debugString(), bind.getCommandStatus());
                String content = "[Registration]" + verifyCode + " is your verification code. Valid in 15 minutes. Please do not share this code with anyone else.";
                SubmitSM submitSM = constructRequest(phonenumber, content);
                //bund faild 会导致TCPIPConnection关闭从而导致outputStream关闭从而导致no
                SubmitSMResp response = session.submit(submitSM);
                log.info("send message result {},command status is {}", response.debugString(), response.getCommandStatus());
            } catch (Exception e) {
                log.error("invoke sms session exception", e);
            }
    
        }
    
        private SubmitSM constructRequest(String phoneNumber, String content) throws WrongLengthOfStringException, UnsupportedEncodingException {
            String recipientPhoneNumber = phoneNumber;
    
            SubmitSM request = new SubmitSM();
            request.setSourceAddr(createAddress("test"));
    
            request.setDestAddr(createAddress(recipientPhoneNumber));
            request.setShortMessage(content, Data.ENC_UTF8);
            request.setReplaceIfPresentFlag((byte) 0);
            request.setEsmClass((byte) 0);
            request.setProtocolId((byte) 0);
            request.setPriorityFlag((byte) 0);
            request.setRegisteredDelivery((byte) 1);// we want delivery reports
            request.setDataCoding((byte) 0);
            request.setSmDefaultMsgId((byte) 0);
            return request;
        }
    
        private Address createAddress(String address) throws WrongLengthOfStringException {
            Address addressInst = new Address();
            // national ton
            addressInst.setTon((byte) 5);
            // numeric plan indicator
            addressInst.setNpi((byte) 0);
            addressInst.setAddress(address, Data.SM_ADDR_LEN);
            return addressInst;
        }
    
    
        /**
         * Redis中存储Value(value可为string,map,list,set等),并设置过期时间
         *
         * @param key
         * @param value
         * @param timeout
         * @param unit
         */
        public void putValueToRedisWithTimeout(Object key, Object value, final long timeout, final TimeUnit unit) {
            try {
                ValueOperations valueOperations = redisTemplate.opsForValue();
                valueOperations.set(key, value, timeout, unit);
            } catch (Exception e) {
            }
        }
    
    
        /**
         * 根据key获取Value值
         *
         * @param key
         */
        public Object getValueByKey(Object key) {
            Object value = null;
            try {
                ValueOperations valueOperations = redisTemplate.opsForValue();
                value = valueOperations.get(key);
            } catch (Exception e) {
            }
            return value;
        }
    
        /**
         * 根据key获取Value值
         *
         * @param key
         */
        public Object deleteKey(Object key) {
            Object value = null;
            try {
                if (redisTemplate.hasKey(key)) {
                    redisTemplate.delete(key);
                }
            } catch (Exception e) {
            }
            return value;
        }
    
    }

    3. 校验验证码

        此处代码就不贴了,直接根据手机号去redis中查询,若有值进行比较是否对错,若无值,直接抛出验证码无效即可

     4. 对接网关的api

         请参考https://www.world-text.com/docs/interfaces/SMPP/ 和 https://www.smssolutions.net/tutorials/smpp/smpperrorcodes/

     5.短信网关模拟器

        对接在测试环境时怎么测试是个问题,从网上找到一个模拟SMPP的,感觉挺好用,此处分享地址:SMPP模拟器 

    其他的参考:

       http://read.pudn.com/downloads97/sourcecode/java/400090/OpenSMPP/src/org/smpp/test/SMPPTest.java__.htm

       https://support.nowsms.com/discus/messages/1/SMPP_v3_4_Issue1_2-24857.pdf

       https://stackoverflow.com/questions/26729958/using-smpp-to-send-sms-texts-in-java?answertab=active#tab-top

       https://www.activexperts.com/sms-component/smpp-specifications/smpp-pdu-definition/

       https://www.openmarket.com/docs/Content/apis/v4smpp/bind.htm

       https://zhuanlan.zhihu.com/p/58237629

  • 相关阅读:
    python返回列表最大值(java返回数组最大值)
    Mysql的5种索引添加类型
    阿里云中quick bi用地图分析数据时维度需转换为地理区域类型
    根据变量查找元素,并修改数值的实践
    Linux 通过命令设置网络
    mysql 实现 上一行减去下一行
    Spark 安装与启动
    Kafka 入门之集群部署遇到问题
    rmp使用方法
    Mysql 导入数据的一种方法
  • 原文地址:https://www.cnblogs.com/guanbin-529/p/12340492.html
Copyright © 2011-2022 走看看