Django中使用极验Geetest滑动验证码

一,环境部署

1.创建一个django测试项目

  此处省略...

 

二,文档部署

1.下载安装python对应的SDK

  使用命令从Github导入完整项目:git clone https://github.com/GeeTeam/gt3-python-sdk.git

  手动下载压缩包文件:https://github.com/GeeTeam/gt3-python-sdk/archive/master.zip

2.参数配置

              修改请求参数(可选)

名称说明
user_id 用户标识,若担心用户信息风险,可作预处理(如哈希处理)再提供
client_type 客户端类型,web(pc浏览器),h5(手机浏览器,包括webview),native(原生app),unknown(未知)
ip_address 客户端请求您服务器的ip地址,unknow表示未知

 

三.代码实现

  SDK:utils>geetest.py

import sys
import random
import json
import requests
import time
from hashlib import md5


if sys.version_info >= (3,):
    xrange = range    

VERSION = "3.0.0"


class GeetestLib(object):

    FN_CHALLENGE = "geetest_challenge"
    FN_VALIDATE = "geetest_validate"
    FN_SECCODE = "geetest_seccode"

    GT_STATUS_SESSION_KEY = "gt_server_status"

    API_URL = "http://api.geetest.com"
    REGISTER_HANDLER = "/register.php"
    VALIDATE_HANDLER = "/validate.php"
    JSON_FORMAT = False

    def __init__(self, captcha_id, private_key):
        self.private_key = private_key
        self.captcha_id = captcha_id
        self.sdk_version = VERSION
        self._response_str = ""


    def pre_process(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
        """
        验证初始化预处理.
        //TO DO  arrage the parameter
        """
        status, challenge = self._register(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
        self._response_str = self._make_response_format(status, challenge,new_captcha)
        return status

    def _register(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
        pri_responce = self._register_challenge(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
        if pri_responce:
            if JSON_FORMAT == 1:
                response_dic = json.loads(pri_responce)
                challenge = response_dic["challenge"]
            else:
                challenge = pri_responce
        else:
            challenge=" "
        if len(challenge) == 32:
            challenge = self._md5_encode("".join([challenge, self.private_key]))
            return 1,challenge
        else:
            return 0, self._make_fail_challenge()

    def get_response_str(self):
        return self._response_str

    def _make_fail_challenge(self):
        rnd1 = random.randint(0, 99)
        rnd2 = random.randint(0, 99)
        md5_str1 = self._md5_encode(str(rnd1))
        md5_str2 = self._md5_encode(str(rnd2))
        challenge = md5_str1 + md5_str2[0:2]
        return challenge

    def _make_response_format(self, success=1, challenge=None,new_captcha=1):
        if not challenge:
            challenge = self._make_fail_challenge()
        if new_captcha:
            string_format = json.dumps(
                {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":True})
        else:
            string_format = json.dumps(
                {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":False})
        return string_format

    def _register_challenge(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
        if user_id:
            register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
                    api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
        else:
            register_url = "{api_url}{handler}?gt={captcha_ID}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
                    api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
        try:
            response = requests.get(register_url, timeout=2)
            if response.status_code == requests.codes.ok:
                res_string = response.text
            else:
                res_string = ""
        except:
            res_string = ""
        return res_string

    def success_validate(self, challenge, validate, seccode, user_id=None,gt=None,data='',userinfo='',JSON_FORMAT=1):
        """
        正常模式的二次验证方式.向geetest server 请求验证结果.
        """
        if not self._check_para(challenge, validate, seccode):
            return 0
        if not self._check_result(challenge, validate):
            return 0
        validate_url = "{api_url}{handler}".format(
            api_url=self.API_URL, handler=self.VALIDATE_HANDLER)
        query = {
            "seccode": seccode,
            "sdk": ''.join( ["python_",self.sdk_version]),
            "user_id": user_id,
            "data":data,
            "timestamp":time.time(),
            "challenge":challenge,
            "userinfo":userinfo,
            "captchaid":gt,
            "json_format":JSON_FORMAT
        }
        backinfo = self._post_values(validate_url, query)
        if JSON_FORMAT == 1:
            backinfo = json.loads(backinfo)
            backinfo = backinfo["seccode"]
        if backinfo == self._md5_encode(seccode):
            return 1
        else:
            return 0

    def _post_values(self, apiserver, data):
        response = requests.post(apiserver, data)
        return response.text

    def _check_result(self, origin, validate):
        encodeStr = self._md5_encode(self.private_key + "geetest" + origin)
        if validate == encodeStr:
            return True
        else:
            return False

    def failback_validate(self, challenge, validate, seccode):
        """
        failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
        """
        if not self._check_para(challenge, validate, seccode):
            return 0
        validate_result = self._failback_check_result(
            challenge, validate,)
        return validate_result

    def _failback_check_result(self,challenge,validate):
        encodeStr = self._md5_encode(challenge)
        if validate == encodeStr:
            return True
        else:
            return False



    def _check_para(self, challenge, validate, seccode):
        return (bool(challenge.strip()) and bool(validate.strip()) and  bool(seccode.strip()))



    def _md5_encode(self, values):
        if type(values) == str:
            values = values.encode()
        m = md5(values)
        return m.hexdigest()

view.py

# _*_ coding=utf-8 _*_
import uuid, json
from rest_framework.views import APIView
from rest_framework.response import Response
from api.models import Account, UserToken
from django_redis import get_redis_connection
from django.http import HttpResponse
from api.utils.geetest import GeetestLib


# id和key需要在Geetest官网自行申请,示例id不可用
pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"
REDIS_CONN = get_redis_connection('default')


class GeetestView(APIView):

    def get(self, request):
        user_id = 'test'
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        status = gt.pre_process(user_id)
        # 使用session
        # request.session[gt.GT_STATUS_SESSION_KEY] = status
        # request.session["user_id"] = user_id
        # 使用redis
        REDIS_CONN.set(gt.GT_STATUS_SESSION_KEY, status)
        REDIS_CONN.set("gt_user_id", user_id)
        response_str = gt.get_response_str()
        return HttpResponse(response_str)

    def post(self, request):
        # print(request.session.get("user_id"))
        print(request.META.get("HTTP_AUTHENTICATION"))
        print(request.data)
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        challenge = request.data.get(gt.FN_CHALLENGE, '')
        validate = request.data.get(gt.FN_VALIDATE, '')
        seccode = request.data.get(gt.FN_SECCODE, '')
        # 验证username,pwd
        # status = request.session.get(gt.GT_STATUS_SESSION_KEY)
        # print(status)
        # user_id = request.session.get("user_id")
        # print(user_id)
        status = REDIS_CONN.get(gt.GT_STATUS_SESSION_KEY)
        user_id = REDIS_CONN.get("gt_user_id")
        if status:
            result = gt.success_validate(challenge, validate, seccode, user_id)
        else:
            result = gt.failback_validate(challenge, validate, seccode)
        result = {"status": "success"} if result else {"status": "fail"}
        # if result:
        #     # 证明验证码通过
        #     # 判断用户名和密码
        # else:
        #     #  返回验证码错误
        return HttpResponse(json.dumps(result))

url.py

path('pc-geetest/register', GeetestView.as_view()),
path('pc-geetest/ajax_validate', GeetestView.as_view()),

 

 

 

login.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="http://static.geetest.com/static/tools/gt.js"></script>
    <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
    <style>
        body {
            margin: 50px 0;
            text-align: center;
        }

        .inp {
            border: 1px solid gray;
            padding: 0 10px;
            width: 200px;
            height: 30px;
            font-size: 18px;
        }

        .btn {
            border: 1px solid gray;
            width: 100px;
            height: 30px;
            font-size: 18px;
            cursor: pointer;
        }

        #embed-captcha {
            width: 300px;
            margin: 0 auto;
        }

        .show {
            display: block;
        }

        .hide {
            display: none;
        }

        #notice {
            color: red;
        }

        /* 以下遮罩层为demo.用户可自行设计实现 */
        #mask {
            display: none;
            position: fixed;
            text-align: center;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            overflow: auto;
        }

        /* 可自行设计实现captcha的位置大小 */
        .popup-mobile {
            position: relative;
        }

        #popup-captcha-mobile {
            position: fixed;
            display: none;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            -webkit-transform: translate(-50%, -50%);
            z-index: 9999;
        }
    </style>


</head>
<body>
<div id="app">
    <div class="popup">
        <h2>弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值</h2>
        <br>
        <p>
            <label>用户名:</label>
            <input id="username1" class="inp" type="text" value="极验验证" v-model="username">
        </p>
        <br>
        <p>
            <label>&nbsp;&nbsp;&nbsp;&nbsp;码:</label>
            <input id="password1" class="inp" type="password" value="123456" v-model="pwd">
        </p>

        <br>
        <input class="btn" id="popup-submit" type="submit" value="提交" ref="popup">

        <div id="popup-captcha"></div>
    </div>
</div>
<script>
    // Vue.prototype.$axios = axios;
    const app = new Vue({
        el: "#app",
        data: {
            username: "极验验证",
            pwd: "123456"
        },
        mounted() {
            let that = this;

            // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback)
            axios.request({
                url: "http://127.0.0.1:8008/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存
                method: "get",
            }).then(function (data) {
                console.log(data.data);
                // 使用initGeetest接口
                // 参数1:配置参数
                // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
                initGeetest({
                    gt: data.data.gt,
                    challenge: data.data.challenge,
                    product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
                    offline: !data.data.success, // 表示用户后台检测极验服务器是否宕机,一般不需要关注
                    new_captcha: true
                    // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
                }, function (captchaObj) {
                    // 成功的回调
                    console.log("进入成功的回调");
                    captchaObj.onSuccess(function () {
                        let validate = captchaObj.getValidate();
                        console.log(122233333)
                        axios.request({
                            url: "http://127.0.0.1:8008/pc-geetest/ajax_validate", // 进行二次验证
                            method: "post",
                            data: {
                                username: that.username,
                                password: that.pwd,
                                geetest_challenge: validate.geetest_challenge,
                                geetest_validate: validate.geetest_validate,
                                geetest_seccode: validate.geetest_seccode
                            }
                        }).then(function (data) {
                            console.log(data.data);
                            if (data && (data.data.status === "success")) {
                                alert("登录成功")
                            } else {
                                alert("登录失败")
                            }
                        })
                    });
                    console.log(that.$refs.popup);
                    that.$refs.popup.onclick = function () {
                        captchaObj.show();
                    };
                    // 将验证码加到id为captcha的元素里
                    captchaObj.appendTo("#popup-captcha");
                    // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html
                });
            })


        }

    })
</script>
</body>
</html>
View Code

 

 

 

posted @ 2019-03-05 20:11  ZivLi  阅读(582)  评论(0编辑  收藏  举报