写点什么

教你 10 分钟解决短信验证码接口被盗刷、轰炸、恶意点击等问题。

发布于: 2021 年 02 月 01 日
教你10分钟解决短信验证码接口被盗刷、轰炸、恶意点击等问题。

一、企业短信防火墙的实现

1.1 简介


新昕科技在交易反欺诈核心上, 通过 AI 快速学习机制,结合国际领先的设备指纹技术,首次推出无需图形验证码机制的企业短信防火墙,三步完成下载对接。

1.2 第一步:获取防火墙帐号密钥


进入 防火墙控制台,在左侧导航栏选择【网站管理】,进入网站管理页面,单击【发到邮箱】接收密钥。


1.3 第二步:下载防火墙服务器


前往新昕科技官网,在顶部导航栏选择【解决方案】>【下载中心】,进入下载中心页面,找到短信防火墙服务器安装包,点击【下载链接】即可下载。


1.4 第三步:业务系统前后端接入

web 前端接入:

前端接入:

Web 前端接入文档


Java 在页面合适的位置(标签内)加入以下代码引入 JS 文件:


<script type="text/javascript" src="/NxtJsServlet"></script>
复制代码

PHP 在页面合适的位置(标签内)加入以下代码引入 JS 文件:

   <script id="finger" type="text/javascript" src="/nxt_inc/nxt_front.php"></script>
复制代码

后端接入:

Java

修改配置(和业务系统同系统不需要修改):

newxtc.ini (存放位置:"/WEB-INF/classes/newxtc.ini")

修改参数(fireWareUrl)--> fireWareUrl=http://localhost:7502

短信下发


public RetMsg smsSend(HttpServletRequest request, HttpServletResponse response, String clientMobile) {    RetMsg retMsg = new RetMsg(-1, "系统异常");    FwClient fwClient = new FwClientImpl();    try {        // 1 调用【短信防火墙】短信发送请求        HashMap < String, Object > paramMap = fwClient.getSendReq(request, clientMobile);        String jsonReq = fwClient.execReq(paramMap);        String smsSendRet = fwClient.getRetVaule(jsonReq, "riskResult");        if("REJECT".equals(smsSendRet)) {            retMsg.setRet(3);            retMsg.setMsg("请求过于频繁");        }        else {            // 业务 TODO            // 业务调用短信接口 TODO            // 调用短信接口 结束            if(smsRetMsg != null && smsRetMsg.getRet() == 0) {                // 2 调用【短信防火墙】成功结果                fwClient.execSucc(paramMap);                logger.debug("send succ");                retMsg.setRet(0);                retMsg.setMsg("发送验证码成功");            }            else {                // 2 调用【短信防火墙】失败结果                SmsVerifyCache.getInstance().remove(clientMobile);                fwClient.execFail(paramMap);                retMsg.setRet(-1);                retMsg.setMsg("发送验证码失败");            }        }    }    catch(Exception e) {        for(StackTraceElement elment: e.getStackTrace()) {            logger.error(elment.toString());        }    }    return retMsg;}
复制代码

短信验证


public RetMsg smsVerify(HttpServletRequest request, HttpServletResponse response, String clientMobile, String smsVerifyCode) {    FwClient fwClient = new FwClientImpl();    RetMsg retMsg = new RetMsg(-1, "系统异常");    if(smsVerifyCode == null || smsVerifyCode.isEmpty()) {        retMsg.setRet(1);        retMsg.setMsg("输入验证码为空");    }    else {        // 1 调用【短信防火墙】验证请求        HashMap < String, Object > paramMap = fwClient.getVerifyReq(request, clientMobile); // 请求防火墙        String jsonReq = fwClient.execReq(paramMap);        // 报文处理        String smsSendRet = fwClient.getRetVaule(jsonReq, "riskResult");        if("REJECT".equals(smsSendRet)) {            retMsg.setRet(3);            retMsg.setMsg("请求过于频繁");        }        // 业务 TODO        if(cacheSmsVerify != null && cacheSmsVerify.getVerifyCode() != null && !cacheSmsVerify.getVerifyCode().isEmpty()) {            if(cacheSmsVerify.getVerifyCode().equals(smsVerifyCode)) {                retMsg.setRet(0);                retMsg.setMsg("验证成功");            }            else {                retMsg.setRet(1);                retMsg.setMsg("验证码错误");            }        }        else {            retMsg.setRet(-9);            retMsg.setMsg("验证码超时");        }        if(retMsg.getRet() == 0) {            // 2 调用【短信防火墙】成功结果            fwClient.execSucc(paramMap);        }        else {            // 2 调用【短信防火墙】失败结果            fwClient.execFail(paramMap);        }    }    return retMsg;}
复制代码


PHP

修改配置文件(和业务系统同系统不需要修改):

nxtini.php (存放位置:"\nxtinc")

修改参数($GLOBALS ["fireWareUrl"])-->$GLOBALS ["fireWareUrl"]="http://localhost:7502"


短信下发

require_once $_SERVER['DOCUMENT_ROOT']."/nxt_inc/nxt_client.php";/** * 发送短信 * @param $mobile */publicfunction send(string $mobile) {    $fwClient = new ClientApi();    // 获取下发短信报文    $paramMap = $fwClient - > getSendReq($phone);    // 执行下发短信请求     $jsonReq = $fwClient - > execReq($paramMap);    $fwRet = $fwClient - > getRetVaule($jsonReq, "riskResult");    if("REJECT" != $fwRet) {        // 发送短信业务 TODO        if(发送成功标记) {            //  下发短信成功            $fwClient - > execSucc($paramMap);        }        else {            //  下发短信失败            $fwClient - > execFail($paramMap);        }    }}
复制代码


短信验证

/** * 短信验证 * @param $mobile */publicfunction smsVerify(string $mobile) {    $fwClient = new ClientApi();    // 获取短信验证报文    $paramMap = $fwClient - > getVerifyReq($phone);    // 执行短信验证请求     $jsonReq = $fwClient - > execReq($paramMap);    $fwRet = $fwClient - > getRetVaule($jsonReq, "riskResult");    if("REJECT" != $fwRet) {        // 验证短信业务 TODO        if(验证成功标记) {            //  验证短信成功            $fwClient - > execSucc($paramMap);        }        else {            //  验证短信失败            $fwClient - > execFail($paramMap);        }    }}
复制代码


1.5 丰富可视化实时风险大盘


防御拦截数据尽收眼底,实时查看当日数据详情与近期风险趋势。

通过风控数据看板,可查看 1-30 天的验证情况、风控拦截情况以及验证事件触发的 AI 模型情况。

进入防火墙控制台,在左侧导航栏选择【风险大盘】,进入风险大盘页面。


二、短信验证码的实现

2.1 简介


企业防火墙只能防止机器人脚本恶意攻击网站或 App,如何识别到是本人操作的,还需要结合短信验证码进一步进行身份识别。本文以注册为例,在 SpringMVC+Spring+Mybatis 框架的基础上完成该短信验证码功能。


发送短信验证码的原理是:随机生成一个 4-6 位数字,将该 4-6 位数字保存到 session 当中,客户端通过 sessionid 判断对应的 session,用户输入的验证码再与 session 记录的验证码进行比较。


一般的第三方短信平台都会有他们自己的短信接口,只要读懂他们的接口稍作稍作改变就能满足自己的需求。


首先将短信平台接口代码列出:这里需要依赖的三个通用 jar 包 commons-logging-1.1.1.jar,commons-httpclient-3.1.jar,commons-codec-1.4.jar。


2.2 短信服务商接入


开发短信验证码功能通常是采用第三方短信服务商的服务,作为短信下发渠道,以这个网站短信验证码为例:上海中昱文化传播有限公司【简称中昱维信】。获取试用验证码条数和验证码接口文档,进行接口对接前需首先进行准备工作:


短信签名报备


短信验证码需预先设置短信签名,签名会经过平台审核,审核通过后才可作为接口参数使用。

短信验证码模板报备


短信验证码需预先设置短信模板,签名会经过平台审核,审核通过后才可作为接口参数使用。


获取 appId 和 appKey


为保障接口安全,短信验证码接口,使用多重加密的 appId 和 appKey 进行身份鉴权和校验,这两个字段作为验证码接口必填项,建议预先获取到,获取 appKey 时需要对注册人进行身份校验。


至此,短信服务商验证码接口已经准备完毕,接下来可以进行业务场景,进行短信验证码服务的开发,下面展示通用的短信验证码接入流程。

2.3 短信验证码代码实现


短信验证码前端较为简单,主要就是发送验证码和校验验证码两个 ajax 请求,结合上面的滑动验证可实现双重验证。前端页面结构如下:


   <html>    <head>        <meta charset="utf-8"/>        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">        <!-- 国内使用 -->        <link rel="stylesheet" href="https://cdn.staticfile.org/amazeui/2.7.2/css/amazeui.min.css">        <script type="text/javascript" charset="utf-8" src="https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js"></script>    </head>    <body>         <div class="am-form">        <div class="am-form-group">            <label for="tel">请输入手机号</label>            <input type="text" class="" id="tel" placeholder="请输入手机号">        </div>             <div id="__nc" style="height: 70px">            <div id="nc"></div>        </div>             <div class="am-form-group">            <label for="code">请输入验证码</label>            <input id="code" type="text" placeholder="请输入验证码">        </div>             <button type="button" class="am-btn am-btn-primary">提交</button>    </div>         <script>        var nc_token = ["CF_APP_1", (new Date()).getTime(), Math.random()].join(':');        var nc=NoCaptcha.init({            renderTo: '#nc',            appkey: 'CF_APP_1',            scene: 'register',            token: nc_token,            trans: {"key1": "code200"},            elementID: ["usernameID"],            is_Opt: 0,            language: "cn",            timeout: 10000,            retryTimes: 5,            errorTimes: 5,            inline:false,            apimap: {    // 'analyze': '//a.com/nocaptcha/analyze.jsonp',    // 'uab_Url': '//aeu.alicdn.com/js/uac/909.js',            },            bannerHidden:false,            initHidden:false,            callback: function (data) {                window.console && console.log(nc_token)                window.console && console.log(data.csessionid)                window.console && console.log(data.sig);                var tel = $('#tel').val();                $.ajax({                    url: "sendCode",                    type: "post",                    data: {                        tel:tel                    },                    dataType: "json",                    success: function (result) {                        if (result.code == 0) {                            alert("验证码已发送!", "green")                        } else {                            alert("发送失败,请稍后再试!");                            nc.reset()                        }                    },                    error: function () {                        alert("系统繁忙,请稍后再试!", "red")                    }                })            },            error: function (s) {            }        });        NoCaptcha.setEnabled(true);        nc.reset();//请务必确保这里调用一次reset()方法             NoCaptcha.upLang('cn', {            'LOADING':"加载中...",//加载            'SLIDER_LABEL': "请向右滑动验证",//等待滑动            'CHECK_Y':"验证通过",//通过            'ERROR_TITLE':"非常抱歉,这出错了...",//拦截            'CHECK_N':"验证未通过", //准备唤醒二次验证            'OVERLAY_INFORM':"经检测你当前操作环境存在风险,请输入验证码",//二次验证            'TIPS_TITLE':"验证码错误,请重新输入"//验证码输错时的提示        });    </script>    </body>    </html>
复制代码


后端代码主要职责是两方面:1)接收生成验证码请求,生成验证码存入 session 中;2)接收校验验证码的请求,将存入 session 的验证码去除,与前端输入的验证码进行比对,比对一致则通过并进行视图转发,比对不一致则告知前端错误原因。主要实现代码如下:


    HttpSession session = req.getSession();    // 验证码有效时间    session.setMaxInactiveInterval(600);    try {        Integer num = RandNumber.getNum();        //  发送验证码通道        Sendsms.Send(num, phone);        session.setAttribute(phone, num);        return R.ok();    } catch (Exception e) {        e.printStackTrace();        logger.error(e.getMessage());        return R.error("fasle");    }
import java.io.Exception; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.PostMethod; public class Sendsms { private static String Url = "https://vip.veesing.com/smsApi/verifyCode"; // 发送短信验证码 public static void Send(Integer num, String mobile) { try { HttpClient client = new HttpClient(); PostMethod method = new PostMethod(Url); client.getParams().setContentCharset("UTF-8"); method.setRequestHeader("ContentType", "application/x-www-form-urlencoded;charset=UTF-8"); NameValuePair[] data = { new NameValuePair("appId", "*********"), new NameValuePair("appKey", "**********"), new NameValuePair("templateId", "*******"), new NameValuePair("mobile", "*******"), new NameValuePair("variables", "*******") }; method.setRequestBody(data); client.executeMethod(method); String submitResult = method.getResponseBodyAsString(); System.out.println(submitResult); } catch (Exception e) { e.printStackTrace(); } } }
HttpSession session = req.getSession(); String yzm = String.valueOf(session.getAttribute(username)); logger.info(yzm); if (yzm == null) { return R.error("验证码错误"); } if (yzm != null && !verifycode.equals(yzm)) { return R.error("验证码错误"); }
复制代码

以上就是 Java 实现企业短信防火墙与短信验证码的全部实现,基于此案例,可实现安全性高、用户体验好的登录注册+企业短信防火墙+短信验证码功能。


原地址:短信接口被恶意调用?【新昕科技】企业短信防火墙+【中昱维信】短信验证码【Java应用实例】


这篇文章到这里就结束了,感谢大佬们驻足观看,大佬们点个关注、点个赞呗~


谢谢大佬~




作者:香芋味的猫丶


发布于: 2021 年 02 月 01 日阅读数: 53
用户头像

还未添加个人签名 2020.12.23 加入

还未添加个人简介

评论

发布
暂无评论
教你10分钟解决短信验证码接口被盗刷、轰炸、恶意点击等问题。