spring boot+ geetest滑动验证

geetest滑动验证

最近公司为了安全,在登录注册,发送短信等需要验证的地方改为用滑动验证,再此记录一下

一、注册账号极验官网
进去以后找到
极验
ID和key值需要记住,代码中需要配置
二、下载demo,我用的是java
主要代码如下:代码中都有注释,就不做详细介绍
这三个是主要的

/**
 * 配置文件,可合理选择properties等方式自行设计
 */
public class GeetestConfig {

    /**
     * 填入自己在极验官网申请的账号id和key
     */
    public static final String GEETEST_ID = "";

    public static final String GEETEST_KEY = "";

}

package com.persional.controller.geetest;

import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

/**
 * sdk lib包,核心逻辑。
 *
 * @author
 */
public class GeetestLib {

    /**
     * 公钥
     */
    private String geetest_id;

    /**
     * 私钥
     */
    private String geetest_key;

    /**
     * 返回数据的封装对象
     */
    private GeetestLibResult libResult;

    /**
     * 调试开关,是否输出调试日志
     */
    private static final boolean IS_DEBUG = true;

    private static final String API_URL = "http://api.geetest.com";

    private static final String REGISTER_URL = "/register.php";

    private static final String VALIDATE_URL = "/validate.php";

    private static final String JSON_FORMAT = "1";

    private static final boolean NEW_CAPTCHA = true;

    public static final String VERSION = "jave-servlet-maven:3.1.0";

    /**
     * 极验二次验证表单传参字段 chllenge
     */
    public static final String GEETEST_CHALLENGE = "geetest_challenge";

    /**
     * 极验二次验证表单传参字段 validate
     */
    public static final String GEETEST_VALIDATE = "geetest_validate";

    /**
     * 极验二次验证表单传参字段 seccode
     */
    public static final String GEETEST_SECCODE = "geetest_seccode";

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

    public GeetestLib(String geetest_id, String geetest_key) {
        this.geetest_id = geetest_id;
        this.geetest_key = geetest_key;
        this.libResult = new GeetestLibResult();
    }

    public void gtlog(String message) {
        if (this.IS_DEBUG) {
            System.out.println("gtlog: " + message);
        }
    }

    /**
     * 一次验证
     */
    public GeetestLibResult register(String digestmod, Map<String, String> paramMap,String userId) {
        this.gtlog(String.format("register(): 开始一次验证, digestmod=%s.", digestmod));
        String origin_challenge = requestRegister(paramMap);
        buildRegisterResult(origin_challenge, digestmod,userId);
        this.gtlog(String.format("register(): 一次验证, lib包返回信息=%s.", this.libResult));
        return this.libResult;
    }

    /**
     * 向极验发送一次验证的请求,GET方式
     */
    private String requestRegister(Map<String, String> paramMap) {
        paramMap.put("gt", this.geetest_id);
        paramMap.put("json_format", this.JSON_FORMAT);
        String register_url = this.API_URL + this.REGISTER_URL;
        this.gtlog(String.format("requestRegister(): 一次验证, 向极验发送请求, url=%s, params=%s.", register_url, paramMap));
        String origin_challenge = null;
        try {
            String resBody = this.httpGet(register_url, paramMap);
            this.gtlog(String.format("requestRegister(): 一次验证, 与极验网络交互正常, 返回body=%s.", resBody));
            JSONObject jsonObject = new JSONObject(resBody);
            origin_challenge = jsonObject.getString("challenge");
        } catch (Exception e) {
            this.gtlog("requestRegister(): 一次验证, 请求异常,后续流程走failback模式, " + e.toString());
            origin_challenge = "";
        }
        return origin_challenge;
    }

    /**
     * 构建一次验证返回数据
     */
    private void buildRegisterResult(String origin_challenge, String digestmod,String userId) {
        // origin_challenge为空或者值为0代表失败
        if (origin_challenge == null || origin_challenge.isEmpty() || "0".equals(origin_challenge)) {
            // 本地随机生成32位字符串
            String challenge = UUID.randomUUID().toString().replaceAll("-", "");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("success", 0);
            jsonObject.put("gt", this.geetest_id);
            jsonObject.put("challenge", challenge);
            jsonObject.put("userId", userId);
            jsonObject.put("new_captcha", this.NEW_CAPTCHA);
            this.libResult.setAll(0, jsonObject.toString(), "请求极验register接口失败,后续流程走failback模式");
        } else {
            String challenge = null;
            if ("md5".equals(digestmod)) {
                challenge = this.md5_encode(origin_challenge + this.geetest_key);
            } else if ("sha256".equals(digestmod)) {
                challenge = this.sha256_encode(origin_challenge + this.geetest_key);
            } else if ("hmac-sha256".equals(digestmod)) {
                challenge = this.hmac_sha256_encode(origin_challenge, this.geetest_key);
            } else {
                challenge = this.md5_encode(origin_challenge + this.geetest_key);
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("success", 1);
            jsonObject.put("gt", this.geetest_id);
            jsonObject.put("challenge", challenge);
            jsonObject.put("userId", userId);
            jsonObject.put("new_captcha", this.NEW_CAPTCHA);
            this.libResult.setAll(1, jsonObject.toString(), "");
        }
    }

    /**
     * 正常流程下(即一次验证请求成功),二次验证
     */
    public GeetestLibResult successValidate(String challenge, String validate, String seccode, Map<String, String> paramMap) {
        this.gtlog(String.format("successValidate(): 开始二次验证 正常模式, challenge=%s, validate=%s, seccode=%s.", challenge, validate, seccode));
        if (!check_param(challenge, validate, seccode)) {
            this.libResult.setAll(0, "", "正常模式,本地校验,参数challenge、validate、seccode不可为空");
        } else {
            String response_seccode = this.requestValidate(challenge, validate, seccode, paramMap);
            if (response_seccode == null || response_seccode.isEmpty()) {
                this.libResult.setAll(0, "", "请求极验validate接口失败");
            } else if ("false".equals(response_seccode)) {
                this.libResult.setAll(0, "", "极验二次验证不通过");
            } else {
                this.libResult.setAll(1, "", "");
            }
        }
        this.gtlog(String.format("successValidate(): 二次验证 正常模式, lib包返回信息=%s.", this.libResult));
        return this.libResult;
    }

    /**
     * 异常流程下(即failback模式),二次验证
     * 注意:由于是failback模式,初衷是保证验证业务不会中断正常业务,所以此处只作简单的参数校验,可自行设计逻辑。
     */
    public GeetestLibResult failValidate(String challenge, String validate, String seccode) {
        this.gtlog(String.format("failValidate(): 开始二次验证 failback模式, challenge=%s, validate=%s, seccode=%s.", challenge, validate, seccode));
        if (!this.check_param(challenge, validate, seccode)) {
            this.libResult.setAll(0, "", "failback模式,本地校验,参数challenge、validate、seccode不可为空.");
        } else {
            this.libResult.setAll(1, "", "");
        }
        this.gtlog(String.format("failValidate(): 二次验证 failback模式, lib包返回信息=%s.", this.libResult));
        return this.libResult;
    }

    /**
     * 向极验发送二次验证的请求,POST方式
     */
    private String requestValidate(String challenge, String validate, String seccode, Map<String, String> paramMap) {
        paramMap.put("seccode", seccode);
        paramMap.put("json_format", this.JSON_FORMAT);
        paramMap.put("challenge", challenge);
        paramMap.put("sdk", this.VERSION);
        paramMap.put("captchaid", this.geetest_id);
        String validate_url = this.API_URL + this.VALIDATE_URL;
        this.gtlog(String.format("requestValidate(): 二次验证 正常模式, 向极验发送请求, url=%s, params=%s.", validate_url, paramMap));
        String response_seccode = null;
        try {
            String resBody = this.httpPost(validate_url, paramMap);
            this.gtlog(String.format("requestValidate(): 二次验证 正常模式, 与极验网络交互正常, 返回body=%s.", resBody));
            JSONObject jsonObject = new JSONObject(resBody);
            response_seccode = jsonObject.getString("seccode");
        } catch (Exception e) {
            this.gtlog(String.format("requestValidate(): 二次验证 正常模式, 请求异常, " + e.toString()));
            response_seccode = "";
        }
        return response_seccode;
    }

    /**
     * 校验二次验证的三个参数,校验通过返回true,校验失败返回false
     */
    private boolean check_param(String challenge, String validate, String seccode) {
        if (challenge == null || challenge.isEmpty() || validate == null || validate.isEmpty() || seccode == null || seccode.isEmpty()) {
            return false;
        }
        return true;
    }

    /**
     * 发送GET请求,获取服务器返回结果
     */
    private String httpGet(String url, Map<String, String> paramMap) throws Exception {
        Iterator<String> it = paramMap.keySet().iterator();
        StringBuilder paramStr = new StringBuilder();
        while (it.hasNext()) {
            String key = it.next();
            if (key == null || key.isEmpty() || paramMap.get(key) == null || paramMap.get(key).isEmpty()) {
                continue;
            }
            paramStr.append("&").append(URLEncoder.encode(key, "utf-8")).append("=").append(URLEncoder.encode(paramMap.get(key), "utf-8"));
        }
        if (paramStr.length() != 0) {
            paramStr.replace(0, 1, "?");
        }
        url += paramStr.toString();
        URL getUrl = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
        connection.setConnectTimeout(2000); // 设置连接主机超时(单位:毫秒)
        connection.setReadTimeout(2000); // 设置从主机读取数据超时(单位:毫秒)
        connection.connect();
        if (connection.getResponseCode() == 200) {
            StringBuilder sb = new StringBuilder();
            InputStream inStream = null;
            byte[] buf = new byte[1024];
            inStream = connection.getInputStream();
            for (int n; (n = inStream.read(buf)) != -1; ) {
                sb.append(new String(buf, 0, n, "UTF-8"));
            }
            inStream.close();
            connection.disconnect();
            return sb.toString();
        }
        return "";
    }

    /**
     * 发送POST请求,获取服务器返回结果
     */
    private String httpPost(String url, Map<String, String> paramMap) throws Exception {
        Iterator<String> it = paramMap.keySet().iterator();
        StringBuilder paramStr = new StringBuilder();
        while (it.hasNext()) {
            String key = it.next();
            if (key == null || key.isEmpty() || paramMap.get(key) == null || paramMap.get(key).isEmpty()) {
                continue;
            }
            paramStr.append("&").append(URLEncoder.encode(key, "utf-8")).append("=").append(URLEncoder.encode(paramMap.get(key), "utf-8"));
        }
        if (paramStr.length() != 0) {
            paramStr.replace(0, 1, "");
        }
        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(paramStr.toString());
        outputStreamWriter.flush();
        outputStreamWriter.close();
        if (connection.getResponseCode() == 200) {
            StringBuilder sb = new StringBuilder();
            InputStream inStream = null;
            byte[] buf = new byte[1024];
            inStream = connection.getInputStream();
            for (int n; (n = inStream.read(buf)) != -1; ) {
                sb.append(new String(buf, 0, n, "UTF-8"));
            }
            inStream.close();
            connection.disconnect();
            return sb.toString();
        }
        return "";
    }

    /**
     * md5 加密
     */
    private String md5_encode(String plainText) {
        String re_md5 = new String();
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(plainText.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuilder sb = new StringBuilder("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    sb.append("0");
                }
                sb.append(Integer.toHexString(i));
            }
            re_md5 = sb.toString();
        } catch (Exception e) {
            this.gtlog("md5_encode(): 发生异常, " + e.toString());
        }
        return re_md5;
    }

    /**
     * sha256加密
     */
    public String sha256_encode(String plainText) {
        MessageDigest messageDigest;
        String encodeStr = new String();
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(plainText.getBytes("UTF-8"));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (Exception e) {
            this.gtlog("sha256_encode(): 发生异常, " + e.toString());
        }
        return encodeStr;
    }

    /**
     * 将byte转为16进制
     */
    private static String byte2Hex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        String temp = null;
        for (int i = 0; i < bytes.length; i++) {
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length() == 1) {
                // 得到一位的进行补0操作
                sb.append("0");
            }
            sb.append(temp);
        }
        return sb.toString();
    }

    /**
     * hmac-sha256 加密
     */
    private String hmac_sha256_encode(String data, String key) {
        String encodeStr = new String();
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            encodeStr = sb.toString();
        } catch (Exception e) {
            this.gtlog("hmac_sha256_encode(): 发生异常, " + e.toString());
        }
        return encodeStr;
    }

}
/**
 * sdk lib包的返回结果信息。
 *
 * @author
 */
public class GeetestLibResult {
    /**
     * 成功失败的标识码,1表示成功,0表示失败
     */
    private int status = 0;

    /**
     * 返回数据,json格式
     */
    private String data = "";

    /**
     * 备注信息,如异常信息等
     */
    private String msg = "";

    public void setStatus(int status) {
        this.status = status;
    }

    public int getStatus() {
        return status;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setAll(int status, String data, String msg) {
        this.setStatus(status);
        this.setData(data);
        this.setMsg(msg);
    }

    @Override
    public String toString() {
        return String.format("GeetestLibResult{status=%s, data=%s, msg=%s}", this.status, this.data, this.msg);
    }
}

import com.persional.controller.geetest.GeetestConfig;
import com.persional.controller.geetest.GeetestLib;
import com.persional.controller.geetest.GeetestLibResult;
import com.persional.jwt.PassToken;
import com.persional.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
 * @Auther fzc
 * @Date 2021/7/2
 */
public class GeetestController {

    @Autowired
    private RedisUtil redisUtil;

    /**
     * @description
     * 第一次验证,进页面则去调用
     * @return:
     * @authon: fzc
     * @date: 2021/7/2 下午4:13
     */
    
    public void StartCaptcha(HttpServletRequest request,
                             HttpServletResponse response) throws IOException {
        GeetestLib gtLib = new GeetestLib(GeetestConfig.GEETEST_ID,
                GeetestConfig.GEETEST_KEY);
        //自定义参数,可选择添加
        String userId=UUID.randomUUID().toString();
        HashMap<String, String> paramMap = new HashMap<String, String>();
        //网站用户id
        paramMap.put("user_id", userId);
        //web:电脑上的浏览器;h5:手机上的浏览器,包括移动应用内完全内置的web_view;native:通过原生SDK植入APP应用的方式
        paramMap.put("client_type", "web");
        //传输用户请求验证时所携带的IP
        paramMap.put("ip_address", "127.0.0.1");

        // 进行一次验证,得到结果
        GeetestLibResult result = gtLib.register("",paramMap,userId);
        // 把生成的userId和第一次验证返回的状态放到redis中,第二次验证需要取用
        redisUtil.set(userId,result.getStatus());
        
        // 注意,此处api1接口存入session,api2会取出使用,格外注意session的存取和分布式环境下的应用场景
        PrintWriter out = response.getWriter();
        // 注意,不要更改返回的结构和值类型,会报错,如果想添加参数,可以去修改第一次验证返回的数据
        out.println(result.getData());
    }


    /**
     * @description
     * 第二次验证,参数为
     * geetest_challenge
     * geetest_validate
     * geetest_seccode
     * @return:
     * @authon: fzc
     * @date: 2021/7/2 下午4:13
     */
    public void verifyLogin(
            Map<String, Object> params,HttpServletResponse response) throws IOException {
        System.out.println("params++"+params);
        GeetestLib gtLib = new GeetestLib(GeetestConfig.GEETEST_ID,
                GeetestConfig.GEETEST_KEY);
                String challenge = String.valueOf(params.get("geetest_challenge"));
        String validate = String.valueOf(params.get("geetest_validate"));
        String seccode = String.valueOf(params.get("geetest_seccode"));
        String userId = String.valueOf(params.get("userId"));
        PrintWriter out = response.getWriter();

        int gt_server_status_code = 0;
       
        try {
            System.out.println("userId++++"+userId);
            System.out.println("seccode++++"+seccode);
            gt_server_status_code = (int) redisUtil.get(userId);
            System.out.println("gt_server_status_code+++"+gt_server_status_code);
        } catch (Exception e) {
            gtLib.gtlog(
                    "SecondValidateServlet.doPost():二次验证validate:session取key发生异常,"
                            + e.toString());
            out.println("{\"status\":\"fail\"}");
            return;
        }

        //自定义参数,可选择添加
        HashMap<String, String> param = new HashMap<String, String>();
        //网站用户id
        param.put("user_id", userId);
        GeetestLibResult result = null;
        // gt_server_status_code 为1代表一次验证register正常,走正常二次验证模式;为0代表一次验证异常,走failback模式
        if (gt_server_status_code == 1) {
            //gt-server正常,向极验服务器发起二次验证
            result = gtLib.successValidate(challenge, validate, seccode, param);
        } else {
            // gt-server非正常情况,进行failback模式本地验证
            result = gtLib.failValidate(challenge, validate, seccode);
        }
        // // 注意,不要更改返回的结构和值类型
        out.println(result.getStatus());
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值