Spring Boot 整合滑动验证

极验是一种利用生物特征与人工智能技术解决人机交互安全问题的技术,旨在解决安全验证问题,例如:账号登录、短信验证、批量注册等,目前极验、网易易盾比较出众。

在这里主要使用的极验Geetest和springboot 框架整合。

1.首先到极验官网注册账号获取ID和KEY,这里赘述。

2.到极验官网下载,使用SDK,点击下载,如果你使用时Git工具,

#git clone https://github.com/GeeTeam/gt3-java-sdk.git

3.引入SDK到Springboot项目中

将sdk项目目录下的GeetestLib.java文件拷入您的项目Untils目录中,代码如下:

package com.blog.utils;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;

/**
 * 极验核心文件
 *
 */
public class GeetestLib {

    protected final String verName = "4.0";
    protected final String sdkLang = "java";

    protected final String apiUrl = "http://api.geetest.com"; 

    protected final String registerUrl = "/register.php";
    protected final String validateUrl = "/validate.php";

    protected final String json_format = "1";

    /**
     * 极验验证二次验证表单数据 chllenge
     */
    public static final String fn_geetest_challenge = "geetest_challenge";

    /**
     * 极验验证二次验证表单数据 validate
     */
    public static final String fn_geetest_validate = "geetest_validate";

    /**
     * 极验验证二次验证表单数据 seccode
     */
    public static final String fn_geetest_seccode = "geetest_seccode";

    /**
     * 公钥
     */
    private String captchaId = "";

    /**
     * 私钥
     */
    private String privateKey = "";

    /**
     * 是否开启新的failback
     */
    private boolean newFailback = false;

    /**
     * 返回字符串
     */
    private String responseStr = "";

    /**
     * 调试开关,是否输出调试日志
     */
    public boolean debugCode = true;

    /**
     * 极验验证API服务状态Session Key
     */
    public String gtServerStatusSessionKey = "gt_server_status";

    /**
     * 带参数构造函数
     *
     * @param captchaId
     * @param privateKey
     */
    public GeetestLib(String captchaId, String privateKey, boolean newFailback) {

        this.captchaId = captchaId;
        this.privateKey = privateKey;
        this.newFailback = newFailback;
    }

    /**
     * 获取本次验证初始化返回字符串
     *
     * @return 初始化结果
     */
    public String getResponseStr() {

        return responseStr;

    }

    public String getVersionInfo() {

        return verName;

    }

    /**
     * 预处理失败后的返回格式串
     *
     * @return
     */
    private String getFailPreProcessRes() {

        Long rnd1 = Math.round(Math.random() * 100);
        Long rnd2 = Math.round(Math.random() * 100);
        String md5Str1 = md5Encode(rnd1 + "");
        String md5Str2 = md5Encode(rnd2 + "");
        String challenge = md5Str1 + md5Str2.substring(0, 2);

        JSONObject jsonObject = new JSONObject();
        try {

            jsonObject.put("success", 0);
            jsonObject.put("gt", this.captchaId);
            jsonObject.put("challenge", challenge);
            jsonObject.put("new_captcha", this.newFailback);

        } catch (JSONException e) {

            gtlog("json dumps error");

        }

        return jsonObject.toString();

    }

    /**
     * 预处理成功后的标准串
     *
     */
    private String getSuccessPreProcessRes(String challenge) {

        gtlog("challenge:" + challenge);

        JSONObject jsonObject = new JSONObject();
        try {

            jsonObject.put("success", 1);
            jsonObject.put("gt", this.captchaId);
            jsonObject.put("challenge", challenge);

        } catch (JSONException e) {

            gtlog("json dumps error");

        }

        return jsonObject.toString();

    }

    /**
     * 验证初始化预处理
     *
     * @return 1表示初始化成功,0表示初始化失败
     */
    public int preProcess(HashMap<String, String> data) {

        if (registerChallenge(data) != 1) {

            this.responseStr = this.getFailPreProcessRes();
            return 0;

        }

        return 1;

    }

    /**
     * 用captchaID进行注册,更新challenge
     *
     * @return 1表示注册成功,0表示注册失败
     */
    private int registerChallenge(HashMap<String, String>data) {

        try {
            String userId = data.get("user_id");
            String clientType = data.get("client_type");
            String ipAddress = data.get("ip_address");

            String getUrl = apiUrl + registerUrl + "?";
            String param = "gt=" + this.captchaId + "&json_format=" + this.json_format;

            if (userId != null){
                param = param + "&user_id=" + userId;
            }
            if (clientType != null){
                param = param + "&client_type=" + clientType;
            }
            if (ipAddress != null){
                param = param + "&ip_address=" + ipAddress;
            }

            gtlog("GET_URL:" + getUrl + param);
            String result_str = readContentFromGet(getUrl + param);
            if (result_str == "fail"){

                gtlog("gtServer register challenge failed");
                return 0;

            }

            gtlog("result:" + result_str);
            JSONObject jsonObject = new JSONObject(result_str);
            String return_challenge = jsonObject.getString("challenge");

            gtlog("return_challenge:" + return_challenge);

            if (return_challenge.length() == 32) {

                this.responseStr = this.getSuccessPreProcessRes(this.md5Encode(return_challenge + this.privateKey));

                return 1;

            }
            else {

                gtlog("gtServer register challenge error");

                return 0;

            }
        } catch (Exception e) {

            gtlog(e.toString());
            gtlog("exception:register api");

        }
        return 0;
    }

    /**
     * 判断一个表单对象值是否为空
     *
     * @param gtObj
     * @return
     */
    protected boolean objIsEmpty(Object gtObj) {

        if (gtObj == null) {

            return true;

        }

        if (gtObj.toString().trim().length() == 0) {

            return true;

        }

        return false;
    }

    /**
     * 检查客户端的请求是否合法,三个只要有一个为空,则判断不合法
     *
     * @param request
     * @return
     */
    private boolean resquestIsLegal(String challenge, String validate, String seccode) {

        if (objIsEmpty(challenge)) {

            return false;

        }

        if (objIsEmpty(validate)) {

            return false;

        }

        if (objIsEmpty(seccode)) {

            return false;

        }

        return true;
    }

    /**
     * 服务正常的情况下使用的验证方式,向gt-server进行二次验证,获取验证结果
     *
     * @param challenge
     * @param validate
     * @param seccode
     * @return 验证结果,1表示验证成功0表示验证失败
     */
    public int enhencedValidateRequest(String challenge, String validate, String seccode, HashMap<String, String> data) {    

        if (!resquestIsLegal(challenge, validate, seccode)) {

            return 0;

        }

        gtlog("request legitimate");

        String userId = data.get("user_id");
        String clientType = data.get("client_type");
        String ipAddress = data.get("ip_address");

        String postUrl = this.apiUrl + this.validateUrl;
        String param = String.format("challenge=%s&validate=%s&seccode=%s&json_format=%s",
                                     challenge, validate, seccode, this.json_format);

        if (userId != null){
            param = param + "&user_id=" + userId;
        }
        if (clientType != null){
            param = param + "&client_type=" + clientType;
        }
        if (ipAddress != null){
            param = param + "&ip_address=" + ipAddress;
        }

        gtlog("param:" + param);

        String response = "";
        try {

            if (validate.length() <= 0) {

                return 0;

            }

            if (!checkResultByPrivate(challenge, validate)) {

                return 0;

            }

            gtlog("checkResultByPrivate");

            response = readContentFromPost(postUrl, param);

            gtlog("response: " + response);

        } catch (Exception e) {

            e.printStackTrace();

        }

        String return_seccode = "";

        try {

            JSONObject return_map = new JSONObject(response);
            return_seccode = return_map.getString("seccode");
            gtlog("md5: " + md5Encode(return_seccode));

            if (return_seccode.equals(md5Encode(seccode))) {

                return 1;

            } else {

                return 0;

            }

        } catch (JSONException e) {

            gtlog("json load error");
            return 0;

        }

    }

    /**
     * failback使用的验证方式
     *
     * @param challenge
     * @param validate
     * @param seccode
     * @return 验证结果,1表示验证成功0表示验证失败
     */
    public int failbackValidateRequest(String challenge, String validate, String seccode) {

        gtlog("in failback validate");

        if (!resquestIsLegal(challenge, validate, seccode)) {
            return 0;
        }
        gtlog("request legitimate");

        return 1;
    }

    /**
     * 输出debug信息,需要开启debugCode
     *
     * @param message
     */
    public void gtlog(String message) {
        if (debugCode) {
            System.out.println("gtlog: " + message);
        }
    }

    protected boolean checkResultByPrivate(String challenge, String validate) {
        String encodeStr = md5Encode(privateKey + "geetest" + challenge);
        return validate.equals(encodeStr);
    }

    /**
     * 发送GET请求,获取服务器返回结果
     *
     * @param getURL
     * @return 服务器返回结果
     * @throws IOException
     */
    private String readContentFromGet(String URL) throws IOException {

        URL getUrl = new URL(URL);
        HttpURLConnection connection = (HttpURLConnection) getUrl
                .openConnection();

        connection.setConnectTimeout(2000);// 设置连接主机超时(单位:毫秒)
        connection.setReadTimeout(2000);// 设置从主机读取数据超时(单位:毫秒)

        // 建立与服务器的连接,并未发送数据
        connection.connect();

        if (connection.getResponseCode() == 200) {
            // 发送数据到服务器并使用Reader读取返回的数据
            StringBuffer sBuffer = new StringBuffer();

            InputStream inStream = null;
            byte[] buf = new byte[1024];
            inStream = connection.getInputStream();
            for (int n; (n = inStream.read(buf)) != -1;) {
                sBuffer.append(new String(buf, 0, n, "UTF-8"));
            }
            inStream.close();
            connection.disconnect();// 断开连接

            return sBuffer.toString();
        }
        else {

            return "fail";
        }
    }

    /**
     * 发送POST请求,获取服务器返回结果
     *
     * @param getURL
     * @return 服务器返回结果
     * @throws IOException
     */
    private String readContentFromPost(String URL, String data) throws IOException {

        gtlog(data);
        URL postUrl = new URL(URL);
        HttpURLConnection connection = (HttpURLConnection) postUrl
                .openConnection();

        connection.setConnectTimeout(2000);// 设置连接主机超时(单位:毫秒)
        connection.setReadTimeout(2000);// 设置从主机读取数据超时(单位:毫秒)
        connection.setRequestMethod("POST");
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

        // 建立与服务器的连接,并未发送数据
        connection.connect();

         OutputStreamWriter outputStreamWriter = new OutputStreamWriter(connection.getOutputStream(), "utf-8");
         outputStreamWriter.write(data);
         outputStreamWriter.flush();
         outputStreamWriter.close();

        if (connection.getResponseCode() == 200) {
            // 发送数据到服务器并使用Reader读取返回的数据
            StringBuffer sBuffer = new StringBuffer();

            InputStream inStream = null;
            byte[] buf = new byte[1024];
            inStream = connection.getInputStream();
            for (int n; (n = inStream.read(buf)) != -1;) {
                sBuffer.append(new String(buf, 0, n, "UTF-8"));
            }
            inStream.close();
            connection.disconnect();// 断开连接

            return sBuffer.toString();
        }
        else {

            return "fail";
        }
    }

    /**
     * md5 加密
     *
     * @time 2014年7月10日 下午3:30:01
     * @param plainText
     * @return
     */
    private String md5Encode(String plainText) {
        String re_md5 = new String();
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(plainText.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }

            re_md5 = buf.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return re_md5;
    }

}

4.配置密钥,修改请求参数

配置密钥

将sdk项目目录下的GeetestConfig.java拷贝到config目录中,从极验管理后台获取您的公钥(id)和私钥(key),并配置。代码如下:

public class GeetestConfig {

    // 填入自己的captcha_id和private_key
    private static final String geetest_id    = "481923ee3a39373a9519bd422de1bfc8";
    private static final String geetest_key   = "3fbbf190b58b3b63dd6474426c3587d2";
    private static final boolean newfailback  = true;

    public static final String getGeetest_id() {
        return geetest_id;
    }

    public static final String getGeetest_key() {
        return geetest_key;
    }

    public static final boolean isnewfailback() {
        return newfailback;
    }
}

5.编写验证控制器方法,代码如下:

package com.blog.controller;

import com.alibaba.fastjson.JSONObject;
import com.dufan.config.GeetestConfig;
import com.dufan.utils.GeetestLib;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

import java.util.HashMap;

/**
 * @author huxiaoguang
 * @DATE 2019年01月18日
 */
@Controller
@RequestMapping("/Geetest")
public class GeetestController
{
    /**
     * 初始化验证码
     * @param
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "initVerify", method = RequestMethod.GET, headers = "Accept=application/json")
    private JSONObject init(HttpServletRequest request)
    {
        GeetestConfig config = new GeetestConfig();
        GeetestLib gtSdk = new GeetestLib(config.getGeetest_id(), config.getGeetest_key(), config.isnewfailback());

        String challenge = request.getParameter(GeetestLib.fn_geetest_challenge);
        String validate  = request.getParameter(GeetestLib.fn_geetest_validate);
        String seccode   = request.getParameter(GeetestLib.fn_geetest_seccode);

        //从session中获取gt-server状态
        int gt_server_status_code = (Integer) request.getSession().getAttribute(gtSdk.gtServerStatusSessionKey);

        //自定义参数,可选择添加
        HashMap<String, String> param = new HashMap<String, String>();
        param.put("user_id",     "blog"); //网站用户id
        param.put("client_type", "h5");
        param.put("ip_address",  "xxx.xx.xx.xxx"); //传输用户请求验证时所携带的IP

        int gtResult = 0;

        if (gt_server_status_code == 1)
        {
            //gt-server正常,向gt-server进行二次验证
            gtResult = gtSdk.enhencedValidateRequest(challenge, validate, seccode, param);
        } else {
            // gt-server非正常情况下,进行failback模式验证
            gtResult = gtSdk.failbackValidateRequest(challenge, validate, seccode);
        }

        JSONObject json = new JSONObject();

        //验证成功
        if (gtResult == 1)
        {
            json.put("status", "success");
            json.put("version", gtSdk.getVersionInfo());
        }
        else {
            json.put("status", "fail");
            json.put("version", gtSdk.getVersionInfo());
        }

        return json;
    }
}

6.前端ajax请求验证
将sdk项目目录下的gt.js拷贝到前端框架中并引入,前端代码如下:

var validate = [];

        //短信登录
        $("#loginbtn-sms").click(function()
        {
            var phone = $("#phone").val();
            var verify = $("#verify").val();
            var code = $("#code").val();

            if(!phone)
            {
                $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>请输入手机号</div>‘);
                return false;
            }else{
                $("#msg").hide().html();
            }

            //验证手机号格式
            if(!isPhoneVaild(phone))
            {
                $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>手机号码格式错误</div>‘);
                return false;
            }else{
                $("#msg").hide().html();
            }

            if(!code)
            {
                $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>请输入短信验证码</div>‘);
                return false;
            }else{
                $("#msg").hide().html();
            }

            //极验验证
            if(validate.length==0)
            {
                $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>请点击按钮进行验证</div>‘);
                return false;
            }else{
                $("#msg").hide().html();
            }

            //禁用注册按钮
            $("#msg").hide().html();
            $(this).attr(‘disabled‘, ‘disabled‘).text(‘登录中...‘);

            $.post($("#login-form").attr(‘action‘),{phone:phone,verify:verify,code:code,validate:validate},
            function(json)
            {
                $("#loginbtn-sms").removeAttr("disabled").text(‘登录‘);
                if(json.status)
                {
                    window.location.href = json.url;
                }else{
                    $(".getimage").attr("src", ‘/home/index/verify.html?t=‘+Math.random());
                    $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>‘+json.msg+‘</div>‘);
                }
            },‘json‘)
        });

        //获取验证码
        get_remain_time = function (btn)
        {
            var step = 59;

            var _res = setInterval(function()
            {
                $(‘#‘+btn).attr("disabled", true);
                $(‘#‘+btn).val(‘重新发送‘ + step);
                step -= 1;
                if(step <= 0)
                {
                    $(‘#‘+btn).removeAttr("disabled");
                    $(‘#‘+btn).val(‘获取验证码‘);
                    clearInterval(_res);
                }
            },1000);
        }

        //验证手机号
        isPhoneVaild = function (phone)
        {
            var myreg = /^[1][3,4,5,7,8][0-9]{9}$/;
            if (!myreg.test(phone))
            {
                return false;
            } else {
                return true;
            }
        }

        $(document).keyup(function(event)
        {
            if(event.keyCode ==13)
            {
                $("#loginbtn").trigger("click");
            }
        });

        //发起验证
        $.get(‘/common/Sms/verify‘,{t: new Date().getTime()},
        function(data)
        {
            initGeetest({
                gt               : data.gt,
                challenge  : data.challenge,
                new_captcha: data.new_captcha,
                product    : "popup",
                offline    : !data.success
            }, handlerEmbed);
        },‘json‘)

        handlerEmbed = function (captchaObj)
        {
            //发送验证码
            $("#sendcode").click(function(e)
            {
                var phone = $("#phone").val();
                if(!phone)
                {
                    $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>请输入手机号</div>‘);
                    return false;
                }

                //验证手机号格式
                if(!isPhoneVaild(phone))
                {
                    $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>手机号码格式错误</div>‘);
                    return false;
                }

                //极验验证码验证
                validate = captchaObj.getValidate();
                if (!validate)
                {
                    $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>请点击按钮完成验证</div>‘);
                    e.preventDefault();
                    return false;
                }
                $.post(‘/common/Sms/verifySend‘, {phone:phone,validate:validate,type:5},
                function(json)
                {
                    if(json.status)
                    {
                        get_remain_time(‘sendcode‘);
                    }else{
                        $("#msg").show().html(‘<div class="msg-error"><i class="iconfont"></i>‘+json.msg+‘</div>‘);
                        return false;
                    }
                },‘json‘)
            })

            captchaObj.appendTo("#embed-captcha");
        };

到此springboot整合滑动验证就完成

原文地址:https://www.cnblogs.com/huxiaoguang/p/10806765.html

时间: 2024-08-30 15:43:45

Spring Boot 整合滑动验证的相关文章

企业分布式微服务云SpringCloud SpringBoot mybatis (十三)Spring Boot整合MyBatis

Spring中整合MyBatis就不多说了,最近大量使用Spring Boot,因此整理一下Spring Boot中整合MyBatis的步骤.搜了一下Spring Boot整合MyBatis的文章,方法都比较老,比较繁琐.查了一下文档,实际已经支持较为简单的整合与使用.下面就来详细介绍如何在Spring Boot中整合MyBatis,并通过注解方式实现映射. 整合MyBatis 新建Spring Boot项目,或以Chapter1为基础来操作 pom.xml中引入依赖 这里用到spring-bo

Spring Kafka和Spring Boot整合实现消息发送与消费简单案例

本文主要分享下Spring Boot和Spring Kafka如何配置整合,实现发送和接收来自Spring Kafka的消息. 先前我已经分享了Kafka的基本介绍与集群环境搭建方法.关于Kafka的介绍请阅读Apache Kafka简介与安装(一),关于Kafka安装请阅读Apache Kafka安装,关于Kafka集群环境搭建请阅读Apache Kafka集群环境搭建 .这里关于服务器环境搭建不在赘述. Spring Kafka整合Spring Boot创建生产者客户端案例 创建一个kafk

Spring Boot整合Elasticsearch

Spring Boot整合Elasticsearch Elasticsearch是一个全文搜索引擎,专门用于处理大型数据集.根据描述,自然而然使用它来存储和搜索应用程序日志.与Logstash和Kibana一起,它是强大的解决方案Elastic Stack的一部分,我之前的一些文章中已经对此进行了描述. 保留应用程序日志不是Elasticsearch的唯一使用场景.它通常用作应用程序的辅助数据库,是一个主关系数据库.如果您必须对大型数据集执行全文搜索或仅存储应用程序不再修改的许多历史记录,这个方

spring boot 整合 quartz 集群环境 实现 动态定时任务配置【原】

最近做了一个spring boot 整合 quartz  实现 动态定时任务配置,在集群环境下运行的 任务.能够对定时任务,动态的进行增删改查,界面效果图如下: 1. 在项目中引入jar 2. 将需要的表导入数据库 官网上有不同数据库的脚本,找到对应的,导入即可 3. java 代码 将quartz 的相关配置文件,配置为暴露bean,方便后期引用. 有一处关键的地方,就是注入spring 上下文,也可以算是一个坑.如果,不注入spring 上下文,那么新添加的定时任务job,是新new 的一个

spring boot 整合spring Data JPA+Spring Security+Thymeleaf框架(上)

最近上班太忙所以耽搁了给大家分享实战springboot 框架的使用. 下面是spring boot 整合多个框架的使用. 首先是准备工作要做好. 第一  导入框架所需的包,我们用的事maven 进行对包的管理. 以上的举例是本人的H5DS的真实的后台管理项目,这个项目正在盛情融资中,各位多多捧点人场.关注一下软件发展的动态,说不定以后就是您的生活不可或缺的软件哟. 点击打开链接.闲话少说.现在切入正题. 第二,写点配置文件 第三,spring data -设计一个简单的po关系,这里需要下载一

spring boot整合jsp的那些坑(spring boot 学习笔记之三)

Spring Boot 整合 Jsp 步骤: 1.新建一个spring boot项目 2.修改pom文件 <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <depend

Spring boot整合jsp

这几天在集中学习Spring boot+Shiro框架,因为之前view层用jsp比较多,所以想在spring boot中配置jsp,但是spring boot官方不推荐使用jsp,因为jsp相对于一些模板引擎,性能都比较低,官方推荐使用thymeleaf,但是Spring boot整合jsp的过程已经完成,在这里记录一下. 这篇博文是在LZ上篇文章spring boot+mybatis整合基础上写的,开发工具仍然是Intellij idea.这篇文章的重点是Intellij idea的设置,否

spring boot 整合kafka 报错 Exception thrown when sending a message with key=&#39;null&#39; and payload=JSON to topic proccess_trading_end: TimeoutException: Failed to update metadata after 60000 ms.

org.springframework.kafka.support.LoggingProducerListener- Exception thrown when sending a message with key='null' and payload='{"dataDts":["20180329","20180328","20180327","20180326","20180323"]

activeMQ入门+spring boot整合activeMQ

最近想要学习MOM(消息中间件:Message Oriented Middleware),就从比较基础的activeMQ学起,rabbitMQ.zeroMQ.rocketMQ.Kafka等后续再去学习. 上面说activeMQ是一种消息中间件,可是为什么要使用activeMQ? 在没有使用JMS的时候,很多应用会出现同步通信(客户端发起请求后需要等待服务端返回结果才能继续执行).客户端服务端耦合.单一点对点(P2P)通信的问题,JMS可以通过面向消息中间件的方式很好的解决了上面的问题. JMS规