【集成】极验验证

来源:互联网 发布:连汝安 知乎 编辑:程序博客网 时间:2024/04/28 18:04

极验验证

极验验证地址

http://www.geetest.com

极验验证用途

防止用户恶意频繁请求服务器,也就是防刷,主要用于安全认证。
看官方文档你会发现,其实就是通过弹出验证码窗口进行验证拦截,根据验证结果你可进行自己的操作。

极验验证的使用集成

此处在官方文档有说明,详情点击http://docs.geetest.com/install/client/android/,此处不做赘述。
在此主要说出集成极验验证时的一些坑,以及处理方案。
基本说明:需要服务器提供两个接口 初始化配置接口/验证接口
进入源码你会发现sdk通过

new GT3GeetestUrl().setCaptchaURL(captchaURL);new GT3GeetestUrl().setValidateURL(validateURL);

将初始化接口/验证接口进行了保存(请自行查看源码),之后通过

gt3GeetestUtils.getGeetest();

进行开始验证,在此通过执行task来对之前配置的两个接口发起网络请求,那么问题来了
1.如何在请求中插入cookie

/**     * 验证回调     */    private static class GTListener implements GT3GeetestUtils.GT3Listener {        private Map<String, String> data = new HashMap<>();        // dialog关闭        @Override        public void gt3CloseDialog() {            isCloseFlag = true;        }        // 点击其他,关闭dialog        @Override        public void gt3CancelDialog() {            isCloseFlag = true;        }        // 验证码加载准备完成        @Override        public void gt3DialogReady() {            EvtLog.e("validate", "gt3DialogReady");        }        // 首次请求返回结果,将getCode拿到的两个数据,在发送验证的时候带回去        // 因为sdk不支持添加cookie,所以用此方法来通知后台是否为同一用户        @Override        public void gt3FirstResult(JSONObject jsonObject) {            try {                data.put("captcha_uid", jsonObject.getString("captcha_uid"));                data.put("status", jsonObject.getString("status"));            } catch (JSONException e) {                e.printStackTrace();            }        }        // 二次请求返回结果        @Override        public Map<String, String> gt3SecondResult() {            return data;        }        @Override        public void gt3GetDialogResult(String result) {        }        // 验证码 验证成功        @Override        public void gt3DialogSuccess() {            isCloseFlag = true;        }        // 验证码 验证失败        @Override        public void gt3DialogOnError() {            isCloseFlag = true;        }        @Override        public void gt3DialogSuccessResult(String result) {        }        @Override        public Map<String, String> captchaHeaders() {            // 此方法只是将cookie相关数据加到header并没有加到cookie,依然会造成后台生成新的seesionid            // 处理方案,后台返回一个唯一标识,在验证时返回 回去//          LZCookieStore cookieStore = new LZCookieStore(FeizaoApp.mConctext);//          List<Cookie> cookies = cookieStore.getCookies();//          Map<String, String> data = new HashMap<>();//          if (cookies != null) {//              for (Cookie cookie ://                      cookies) {//                  data.put(cookie.getName(), cookie.getValue());//              }//          }//          return data;            return null;        }    }

在sdk提供的回调中captchaHeaders()是给 初始化配置接口请求时添加header,但是并未单独放入cookie下面,所以需要自己解析。
但实际应用场景一般会在cookie下面放入一些参数用于判断是否为同一用户下的请求,所以在这里有两个解决方案
1. 后台在此接口上做特殊判断,在header中直接去拿,我们通过captchaHeaders()将参数放入header;
2. 我们初始化请求时服务器返回一个参数作为唯一标识,在验证validate的接口时,将此参数值带入到请求body中。
在此段代码中,也就是我当前的项目中使用的是第二种方式进行的处理
2.如何避免谈起多个dialog
sdk通过geetestUtils.getGeetest();来开启验证的,那么我们看一下它的源码

public void getGeetest() {        getLight = GT3LifecycleCallBacks.getInstance(context).gt3SendMsg();        GT3LifecycleCallBacks.getInstance(context).clearAllMsg();        senMsg = new GT3AroundMsg(context).gt3SendSenMsg();        new GT3AroundMsg(context).endSensor();        mGtAppDlgTask = new GtAppDlgTask();        mGtAppDlgTask.execute();        if (!((Activity) context).isFinishing()) {            dialog = new GT3GtDialog(context);            Window dialogWindow = dialog.getWindow();            WindowManager.LayoutParams lp = dialogWindow.getAttributes();            dialogWindow.setGravity(Gravity.CENTER);            dialogWindow.setAttributes(lp);            dialog.show();            dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {                @Override                public void onCancel(DialogInterface dialo) {                    gtListener.gt3CancelDialog();                }            });            dialog.setGtListener(new GT3GtDialog.GtListener() {                @Override                public void gtResult(boolean success, String result) {                    gtListener.gt3GetDialogResult(result);                    if (success) {                        mGtAppValidateTask = new GtAppValidateTask();                        mGtAppValidateTask.execute(result);                    } else {                        //TODO 验证失败                        dialog.shakeDialog();                    }                }                @Override                public void success() {                    gtListener.gt3DialogSuccess();                }                @Override                public void gtCallReady(Boolean status) {                    if (status) {                        //TODO 验证加载完成                        gtListener.gt3DialogReady();                    } else {                        //TODO 验证加载超时,未准备完成                    }                }                @Override                public void gtCallClose() {                    gtListener.gt3CloseDialog();                }                @Override                public void gtError() {                    gtListener.gt3DialogOnError();                }            });        }        captcha.setTimeout(5000);        captcha.setGeetestListener(new GT3Geetest.GeetestListener() {            @Override            public void readContentTimeout() {                mGtAppDlgTask.cancel(true);                //TODO 获取验证参数超时                //Looper.prepare() & Looper.loop(): 在当前线程并没有绑定Looper时返回为null, 可以与toastMsg()一同在正式版本移除                Looper.prepare();//                    toastMsg("read content time out");                Looper.loop();            }            @Override            public void submitPostDataTimeout() {                mGtAppValidateTask.cancel(true);                //TODO 提交二次验证超时//                    toastMsg("submit error");            }            @Override            public void receiveInvalidParameters() {                //TODO 从API接收到无效的JSON参数//                    toastMsg("Did recieve invalid parameters.");            }        });    }

观察源码你会发现,
1. 只要当前activity未停止,只要你调用此方法就会打开新的dialog,并且sdk未提供dialog手动销毁的方法。所以在此你会遇到的问题是:当你请求多个接口并且都需要验证时,它会谈起多个对话框,影响用户体验。在此我做了一个处理,代码如下:

package packagename.common;import android.app.Activity;import com.efeizao.feizao.library.util.EvtLog;import com.example.sdk.GT3GeetestUtils;import org.json.JSONException;import org.json.JSONObject;import java.lang.ref.WeakReference;import java.util.HashMap;import java.util.Map;/** * Created by Elvis on 2017/6/21. * description : 使用极验验证请求,操作是否频繁,存在自动刷请求 */public class GTValidateRequest {    private static GT3GeetestUtils geetestUtils = null;    private static GTValidateRequest gtValidateRequest = null;    private static boolean isCloseFlag = true;      //如果dialog被关闭则为true,否则为false    public static GTValidateRequest getInstance() {        if (gtValidateRequest == null) {            gtValidateRequest = new GTValidateRequest();        }        return gtValidateRequest;    }    /**     * @param context 上下文activity,用于第三方sdk使用(第三方仅支持activity作为上下文对象)     */    public void validate(WeakReference<Activity> context) {        EvtLog.e("validate", "activity:" + context.get().getComponentName().getClassName());        // dialog已经关闭        if (isCloseFlag) {            isCloseFlag = false;            if (geetestUtils == null) {                geetestUtils = new GT3GeetestUtils(context.get());            }            geetestUtils.setGtListener(new GTListener());            geetestUtils.getGeetest();        }    }    /**     * 验证回调     */    private static class GTListener implements GT3GeetestUtils.GT3Listener {        private Map<String, String> data = new HashMap<>();        // dialog关闭        @Override        public void gt3CloseDialog() {            isCloseFlag = true;        }        // 点击其他,关闭dialog        @Override        public void gt3CancelDialog() {            isCloseFlag = true;        }        // 验证码加载准备完成        @Override        public void gt3DialogReady() {            EvtLog.e("validate", "gt3DialogReady");        }        // 首次请求返回结果,将getCode拿到的两个数据,在发送验证的时候带回去        // 因为sdk不支持添加cookie,所以用此方法来通知后台是否为同一用户        @Override        public void gt3FirstResult(JSONObject jsonObject) {            try {                data.put("captcha_uid", jsonObject.getString("captcha_uid"));                data.put("status", jsonObject.getString("status"));            } catch (JSONException e) {                e.printStackTrace();            }        }        // 二次请求返回结果        @Override        public Map<String, String> gt3SecondResult() {            return data;        }        @Override        public void gt3GetDialogResult(String result) {        }        // 验证码 验证成功        @Override        public void gt3DialogSuccess() {            isCloseFlag = true;        }        // 验证码 验证失败        @Override        public void gt3DialogOnError() {            isCloseFlag = true;        }        @Override        public void gt3DialogSuccessResult(String result) {        }        @Override        public Map<String, String> captchaHeaders() {            // 此方法只是将cookie相关数据加到header并没有加到cookie,依然会造成后台生成新的seesionid            // 处理方案,后台返回一个唯一标识,在验证时返回 回去//          LZCookieStore cookieStore = new LZCookieStore(FeizaoApp.mConctext);//          List<Cookie> cookies = cookieStore.getCookies();//          Map<String, String> data = new HashMap<>();//          if (cookies != null) {//              for (Cookie cookie ://                      cookies) {//                  data.put(cookie.getName(), cookie.getValue());//              }//          }//          return data;            return null;        }    }}

在代码中我添加了一个boolean作为标识,用来判断dialog是否被关闭。
默认为关闭,也就是默认允许弹出dialog,当弹出后,状态设置为false,说明已经打开dialog;然后在它的监听中设置状态,gt3CloseDialog()/gt3CancelDialog()/gt3DialogSuccess()/gt3DialogOnError(),这几中情况dialog都会被关闭,我们在此将状态设置为true。这样就可以用来避免dialog的多次打开。(此方案之适用于之前dialog依赖的activity已经销毁。当activity销毁时,dialog也会销毁。如果存在未销毁情况,你只需要在此方案上加入activity是否为同一个的判断就好)。
3. 初始化GT3GeetestUtils
看源码(片段2)中你会发现,他在验证时使用上下文,将其强制转换为activity,所以这里,在初始化时必须传入的是 Activity,那么问题来了,很多时候我们不会直接将其放到activity去调用,更多的是写为一个工具类,在公共的比如网络请求的地方去调用,那么这个时候我们如何获取activity对象呢,在此我直列出代码,在全局保存一个activity来进行随时使用

/**     * 获取栈顶activity     * 通过注册activity的生命周期监听获取最新的栈顶activity     */     private static WeakReference<Activity> app_activity;    private void initGlobeActivity() {        getApplication().registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {            @Override            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {                app_activity = new WeakReference<>(activity);                Log.e("onActivityCreated===", app_activity + "");            }            @Override            public void onActivityDestroyed(Activity activity) {            }            /** Unused implementation **/            @Override            public void onActivityStarted(Activity activity) {                app_activity = new WeakReference<>(activity);            }            @Override            public void onActivityResumed(Activity activity) {                app_activity = new WeakReference<>(activity);            }            @Override            public void onActivityPaused(Activity activity) {            }            @Override            public void onActivityStopped(Activity activity) {            }            @Override            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {            }        });    }

在此我们就拿到了栈顶的activity,记得一定要用弱引用。

结束语

在此极验验证的集成问题已经介绍完了,如果有什么疑问可以博客留言或者发送邮件到elvis@guojiang.tv。
如果觉得可以帮到你那么就拉倒底部,赞一个。
实现方式已粘贴到博客,所以在此就不下发下载链接了。

原创粉丝点击