# !/usr/bin/env python # -*- coding:utf-8 -*- import sys import random import json import requests import time from hashlib import md5 if sys.version_info >= (3,): xrange = range VERSION = "3.2.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" 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): """ 验证初始化预处理. """ status, challenge = self._register(user_id) self._response_str = self._make_response_format(status, challenge) return status def _register(self, user_id=None): challenge = self._register_challenge(user_id) 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): if not challenge: challenge = self._make_fail_challenge() string_format = json.dumps( {'success': success, 'gt': self.captcha_id, 'challenge': challenge}) return string_format def _register_challenge(self, user_id=None): if user_id: register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}".format( api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id) else: register_url = "{api_url}{handler}?gt={captcha_ID}".format( api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id) 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=''): """ 正常模式的二次验证方式.向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 } backinfo = self._post_values(validate_url, query) 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_str = validate.split('_') encode_ans = validate_str[0] encode_fbii = validate_str[1] encode_igi = validate_str[2] decode_ans = self._decode_response(challenge, encode_ans) decode_fbii = self._decode_response(challenge, encode_fbii) decode_igi = self._decode_response(challenge, encode_igi) validate_result = self._validate_fail_image( decode_ans, decode_fbii, decode_igi) return validate_result def _check_para(self, challenge, validate, seccode): return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip())) def _validate_fail_image(self, ans, full_bg_index, img_grp_index): thread = 3 full_bg_name = str(self._md5_encode(str(full_bg_index)))[0:10] bg_name = str(self._md5_encode(str(img_grp_index)))[10:20] answer_decode = "" for i in range(0, 9): if i % 2 == 0: answer_decode += full_bg_name[i] elif i % 2 == 1: answer_decode += bg_name[i] x_decode = answer_decode[4:] x_int = int(x_decode, 16) result = x_int % 200 if result < 40: result = 40 if abs(ans - result) < thread: return 1 else: return 0 def _md5_encode(self, values): if type(values) == str: values = values.encode() m = md5(values) return m.hexdigest() def _decode_rand_base(self, challenge): str_base = challenge[32:] i = 0 temp_array = [] for i in xrange(len(str_base)): temp_char = str_base[i] temp_ascii = ord(temp_char) result = temp_ascii - 87 if temp_ascii > 57 else temp_ascii - 48 temp_array.append(result) decode_res = temp_array[0] * 36 + temp_array[1] return decode_res def _decode_response(self, challenge, userresponse): if len(userresponse) > 100: return 0 shuzi = (1, 2, 5, 10, 50) chongfu = set() key = {} count = 0 for i in challenge: if i in chongfu: continue else: value = shuzi[count % 5] chongfu.add(i) count += 1 key.update({i: value}) res = 0 for i in userresponse: res += key.get(i, 0) res = res - self._decode_rand_base(challenge) return res