├── .gitignore ├── README.rst ├── demo ├── django_demo │ ├── app │ │ ├── __init__.py │ │ └── views.py │ ├── db.sqlite3 │ ├── django_demo │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ └── static │ │ └── index.html ├── flask_demo │ ├── start.py │ └── templates │ │ ├── .login.html.swp │ │ ├── login.html │ │ └── old_login.html └── tornado_demo │ ├── start.py │ └── static │ ├── gt.js │ └── login.html ├── geetest ├── __init__.py └── geetest.py ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.py~ -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Gt Python SDK 2 | =============== 3 | 使用 3.1 之前版本SDK的用户如果想更新到3.1以及以后版本请先联系极验客服,因为为了兼容老用户,新的特性需要修改验证设置。 4 | 5 | 极验验证的Python SDK目前提供基于django, flask, tornado框架的DEMO。 6 | 7 | 本项目是面向服务器端的,具体使用可以参考我们的 `文档 `_ ,客户端相关开发请参考我们的 `前端文档。 `_. 8 | 9 | 开发环境 10 | ---------------- 11 | 12 | - Python (推荐2.7.0以上版本) 13 | - django, flask, tornado框架 14 | 15 | 快速开始 16 | --------------- 17 | 18 | 下面使用示例代码的均以flask框架为例。 19 | 20 | 1. 获取代码 21 | 22 | 从 `Github `__ 上Clone代码: 23 | 24 | .. code-block:: bash 25 | 26 | $ git clone https://github.com/GeeTeam/gt-python-sdk.git 27 | 28 | 2. 安装GeetestSDK 29 | 30 | .. code-block:: bash 31 | 32 | $ sudo python setup.py install 33 | 34 | 3. 初始化验证 35 | 36 | 37 | 在调用GeetestLib前请自行设定公钥和私钥,用户id为可选项,默认为随机数字: 38 | 39 | .. code-block :: python 40 | 41 | captach_id = "你的公钥" 42 | private_key = "你的私钥" 43 | user_id = random.randint(1,100) 44 | 45 | 根据自己的私钥初始化验证 46 | 47 | .. code-block :: python 48 | 49 | @app.route('/getcaptcha', methods=["GET"]) 50 | def get_captcha(): 51 | user_id = random.randint(1,100) 52 | gt = GeetestLib(captcha_id, private_key) 53 | status = gt.pre_process(user_id) 54 | session[gt.GT_STATUS_SESSION_KEY] = status 55 | session["user_id"] = user_id 56 | response_str = gt.get_response_str() 57 | return response_str 58 | 59 | 4. 二次验证 60 | 61 | .. code-block :: python 62 | 63 | @app.route('/validate', methods=["POST"]) 64 | def validate_capthca(): 65 | gt = GeetestLib(captcha_id, private_key) 66 | status = session[gt.GT_STATUS_SESSION_KEY] 67 | challenge = request.form[gt.FN_CHALLENGE] 68 | validate = request.form[gt.FN_VALIDATE] 69 | seccode = request.form[gt.FN_SECCODE] 70 | user_id = session["user_id"] 71 | if status: 72 | result = gt.success_validate(challenge, validate, seccode, user_id) 73 | else: 74 | result = gt.fail_validate(challenge, validate, seccode) 75 | result = "success" if result else "fail" 76 | return result 77 | 78 | 79 | 运行demo 80 | --------------------- 81 | 82 | 1. django demo运行:进入django_demo文件夹,运行: 83 | 84 | .. code-block:: bash 85 | 86 | $ python manage.py runserver 0.0.0.0:8000 87 | 88 | 在浏览器中访问http://localhost:8000即可看到Demo界面 89 | 90 | 2. flask demo运行:进入flask_demo文件夹,运行: 91 | 92 | .. code-block:: bash 93 | 94 | $ python start.py 95 | 96 | 在浏览器中访问http://localhost:5000即可看到Demo界面 97 | 98 | 3. tornado demo运行:进入tornado_demo文件夹,运行: 99 | 100 | .. code-block:: bash 101 | 102 | $ python start.py 103 | 104 | 在浏览器中访问http://localhost:8088即可看到Demo界面 105 | 106 | 107 | 发布日志 108 | ----------------- 109 | + 3.2.0 110 | 111 | - 添加用户标识(user_id)的接口 112 | 113 | + 3.1.2 114 | 115 | - 支持Python3 116 | 117 | + 3.1.1 118 | 119 | - 统一接口 120 | 121 | + 3.1.0 122 | 123 | - 添加challenge加密特性,使验证更安全, 老版本更新请先联系管理员 124 | 125 | + 3.0.1 126 | 127 | - 修复failback情况下 无法正确解码答案的错误 128 | 129 | + 3.0.0 130 | 131 | - 去除SDK对Session操作, 现在Session部分由开发者自己处理 132 | - 简易化初始化过程. 133 | - 修复failback模式BUG 134 | -------------------------------------------------------------------------------- /demo/django_demo/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeeTeam/gt-python-sdk/93469795860b97e1198d2c16e489dbd677bc4b21/demo/django_demo/app/__init__.py -------------------------------------------------------------------------------- /demo/django_demo/app/views.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import json 3 | 4 | from django.shortcuts import render_to_response, RequestContext 5 | from django.http import HttpResponse 6 | from geetest import GeetestLib 7 | 8 | 9 | captcha_id = "b46d1900d0a894591916ea94ea91bd2c" 10 | private_key = "36fc3fe98530eea08dfc6ce76e3d24c4" 11 | 12 | 13 | def home(request): 14 | return render_to_response("index.html", context_instance=RequestContext(request)) 15 | 16 | 17 | def getcaptcha(request): 18 | user_id = 'test' 19 | gt = GeetestLib(captcha_id, private_key) 20 | status = gt.pre_process(user_id) 21 | request.session[gt.GT_STATUS_SESSION_KEY] = status 22 | request.session["user_id"] = user_id 23 | response_str = gt.get_response_str() 24 | return HttpResponse(response_str) 25 | 26 | 27 | def validate(request): 28 | if request.method == "POST": 29 | gt = GeetestLib(captcha_id, private_key) 30 | challenge = request.POST.get(gt.FN_CHALLENGE, '') 31 | validate = request.POST.get(gt.FN_VALIDATE, '') 32 | seccode = request.POST.get(gt.FN_SECCODE, '') 33 | status = request.session[gt.GT_STATUS_SESSION_KEY] 34 | user_id = request.session["user_id"] 35 | if status: 36 | result = gt.success_validate(challenge, validate, seccode, user_id) 37 | else: 38 | result = gt.failback_validate(challenge, validate, seccode) 39 | result = "

登录成功

" if result else "

登录失败

" 40 | return HttpResponse(result) 41 | return HttpResponse("error") 42 | 43 | def ajax_validate(request): 44 | if request.method == "POST": 45 | gt = GeetestLib(captcha_id, private_key) 46 | challenge = request.POST.get(gt.FN_CHALLENGE, '') 47 | validate = request.POST.get(gt.FN_VALIDATE, '') 48 | seccode = request.POST.get(gt.FN_SECCODE, '') 49 | status = request.session[gt.GT_STATUS_SESSION_KEY] 50 | user_id = request.session["user_id"] 51 | if status: 52 | result = gt.success_validate(challenge, validate, seccode, user_id) 53 | else: 54 | result = gt.failback_validate(challenge, validate, seccode) 55 | result = {"status":"success"} if result else {"status":"fail"} 56 | return HttpResponse(json.dumps(result)) 57 | return HttpResponse("error") 58 | -------------------------------------------------------------------------------- /demo/django_demo/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeeTeam/gt-python-sdk/93469795860b97e1198d2c16e489dbd677bc4b21/demo/django_demo/db.sqlite3 -------------------------------------------------------------------------------- /demo/django_demo/django_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeeTeam/gt-python-sdk/93469795860b97e1198d2c16e489dbd677bc4b21/demo/django_demo/django_demo/__init__.py -------------------------------------------------------------------------------- /demo/django_demo/django_demo/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_demo project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.7/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'g-^epx6*uxh!mv6#fwdy9m(xi2-+a*3g2wi73fuevua$m00_3o' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | TEMPLATE_DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'app', 40 | ) 41 | 42 | MIDDLEWARE_CLASSES = ( 43 | 'django.contrib.sessions.middleware.SessionMiddleware', 44 | 'django.middleware.common.CommonMiddleware', 45 | #'django.middleware.csrf.CsrfViewMiddleware', 46 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 47 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ) 51 | 52 | ROOT_URLCONF = 'django_demo.urls' 53 | 54 | WSGI_APPLICATION = 'django_demo.wsgi.application' 55 | 56 | 57 | # Database 58 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 59 | 60 | DATABASES = { 61 | 'default': { 62 | 'ENGINE': 'django.db.backends.sqlite3', 63 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 64 | } 65 | } 66 | 67 | # Internationalization 68 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 69 | 70 | LANGUAGE_CODE = 'en-us' 71 | 72 | TIME_ZONE = 'UTC' 73 | 74 | USE_I18N = True 75 | 76 | USE_L10N = True 77 | 78 | USE_TZ = True 79 | 80 | 81 | # Static files (CSS, JavaScript, Images) 82 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 83 | 84 | STATIC_URL = '/static/' 85 | 86 | TEMPLATE_DIRS = ( 87 | "static", 88 | ) -------------------------------------------------------------------------------- /demo/django_demo/django_demo/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns('', 4 | url(r'^register', 'app.views.getcaptcha', name='getcaptcha'), 5 | url(r'^validate$', 'app.views.validate', name='validate'), 6 | url(r'^ajax_validate','app.views.ajax_validate', name='ajax_validate'), 7 | url(r'/*', 'app.views.home', name='home'), 8 | ) 9 | -------------------------------------------------------------------------------- /demo/django_demo/django_demo/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_demo project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_demo.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /demo/django_demo/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_demo.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /demo/django_demo/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 极验行为式验证 Django 类网站安装测试页面 5 | 38 | 39 | 40 |

极验验证SDKDemo

41 |

42 |
43 |

44 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 119 |

120 |
121 |

122 | 143 | 144 | 181 | 182 | -------------------------------------------------------------------------------- /demo/flask_demo/start.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import json 3 | 4 | from flask import session, make_response, Flask, request, render_template 5 | from geetest import GeetestLib 6 | 7 | pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c" 8 | pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" 9 | mobile_geetest_id = "7c25da6fe21944cfe507d2f9876775a9" 10 | mobile_geetest_key = "f5883f4ee3bd4fa8caec67941de1b903" 11 | app = Flask(__name__) 12 | app.config.update( 13 | DEBUG=True, 14 | ) 15 | 16 | 17 | @app.route('/pc-geetest/register', methods=["GET"]) 18 | def get_pc_captcha(): 19 | user_id = 'test' 20 | gt = GeetestLib(pc_geetest_id, pc_geetest_key) 21 | status = gt.pre_process(user_id) 22 | session[gt.GT_STATUS_SESSION_KEY] = status 23 | session["user_id"] = user_id 24 | response_str = gt.get_response_str() 25 | return response_str 26 | 27 | @app.route('/mobile-geetest/register', methods=["GET"]) 28 | def get_mobile_captcha(): 29 | user_id = 'test' 30 | gt = GeetestLib(mobile_geetest_id, mobile_geetest_key) 31 | status = gt.pre_process(user_id) 32 | session[gt.GT_STATUS_SESSION_KEY] = status 33 | session["user_id"] = user_id 34 | response_str = gt.get_response_str() 35 | return response_str 36 | 37 | @app.route('/pc-geetest/validate', methods=["POST"]) 38 | def pc_validate_captcha(): 39 | gt = GeetestLib(pc_geetest_id, pc_geetest_key) 40 | challenge = request.form[gt.FN_CHALLENGE] 41 | validate = request.form[gt.FN_VALIDATE] 42 | seccode = request.form[gt.FN_SECCODE] 43 | status = session[gt.GT_STATUS_SESSION_KEY] 44 | user_id = session["user_id"] 45 | if status: 46 | result = gt.success_validate(challenge, validate, seccode, user_id) 47 | else: 48 | result = gt.failback_validate(challenge, validate, seccode) 49 | result = "

登录成功

" if result else "

登录失败

" 50 | return result 51 | 52 | @app.route('/pc-geetest/ajax_validate', methods=["POST"]) 53 | def pc_ajax_validate(): 54 | gt = GeetestLib(pc_geetest_id,pc_geetest_key) 55 | challenge = request.form[gt.FN_CHALLENGE] 56 | validate = request.form[gt.FN_VALIDATE] 57 | seccode = request.form[gt.FN_SECCODE] 58 | status = session[gt.GT_STATUS_SESSION_KEY] 59 | user_id = session["user_id"] 60 | if status: 61 | result = gt.success_validate(challenge, validate, seccode, user_id,data='',userinfo='') 62 | else: 63 | result = gt.failback_validate(challenge, validate, seccode) 64 | result = {"status":"success"} if result else {"status":"fail"} 65 | return json.dumps(result) 66 | 67 | @app.route('/mobile-geetest/ajax_validate', methods=["POST"]) 68 | def mobile_ajax_validate(): 69 | gt = GeetestLib(mobile_geetest_id,mobile_geetest_key) 70 | challenge = request.form[gt.FN_CHALLENGE] 71 | validate = request.form[gt.FN_VALIDATE] 72 | seccode = request.form[gt.FN_SECCODE] 73 | status = session[gt.GT_STATUS_SESSION_KEY] 74 | user_id = session["user_id"] 75 | if status: 76 | result = gt.success_validate(challenge, validate, seccode, user_id,data='',userinfo='') 77 | else: 78 | result = gt.failback_validate(challenge, validate, seccode) 79 | result = {"status":"success"} if result else {"status":"fail"} 80 | return json.dumps(result) 81 | 82 | @app.route('/') 83 | def login(): 84 | return render_template('login.html') 85 | 86 | 87 | if __name__ == '__main__': 88 | app.secret_key = 'i-like-python-nmba' 89 | app.run() 90 | -------------------------------------------------------------------------------- /demo/flask_demo/templates/.login.html.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeeTeam/gt-python-sdk/93469795860b97e1198d2c16e489dbd677bc4b21/demo/flask_demo/templates/.login.html.swp -------------------------------------------------------------------------------- /demo/flask_demo/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | gt-python-flask-demo 7 | 65 | 66 | 67 |

极验验证SDKDemo

68 |

69 |
70 |

71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 99 | 100 | 151 |

152 |
153 |

154 | 174 | 175 | 213 |

214 |
215 |

216 | 233 | 234 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /demo/flask_demo/templates/old_login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 极验行为式验证 Flask 类网站安装测试页面 5 | 38 | 39 | 40 |

极验验证SDKDemo

41 |

42 |
43 |

44 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 119 |

120 |
121 |

122 | 143 | 144 | 181 |

182 |
183 |

184 | 201 | 202 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /demo/tornado_demo/start.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import tornado.ioloop 3 | import tornado.web 4 | import tornado.gen 5 | from torndsession.sessionhandler import SessionBaseHandler 6 | from geetest import GeetestLib 7 | 8 | import json 9 | 10 | pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c" 11 | pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" 12 | mobile_geetest_id = "7c25da6fe21944cfe507d2f9876775a9" 13 | mobile_geetest_key = "f5883f4ee3bd4fa8caec67941de1b903" 14 | 15 | product = "embed" 16 | # 弹出式 17 | # product = "popup&popupbtnid=submit-button" 18 | 19 | 20 | class MainHandler(tornado.web.RequestHandler): 21 | def get(self): 22 | self.render("static/login.html",) 23 | class JsHandler(tornado.web.RequestHandler): 24 | def get(self): 25 | self.render("static/gt.js",) 26 | 27 | class PcGetCaptchaHandler(SessionBaseHandler): 28 | def get(self): 29 | user_id = 'test' 30 | gt = GeetestLib(pc_geetest_id, pc_geetest_key) 31 | status = gt.pre_process(user_id) 32 | self.session[gt.GT_STATUS_SESSION_KEY] = status 33 | self.session["user_id"] = user_id 34 | response_str = gt.get_response_str() 35 | self.write(response_str) 36 | 37 | class MobileGetCaptchaHandler(SessionBaseHandler): 38 | def get(self): 39 | user_id = 'test' 40 | gt = GeetestLib(mobile_geetest_id, mobile_geetest_key) 41 | status = gt.pre_process(user_id) 42 | self.session[gt.GT_STATUS_SESSION_KEY] = status 43 | self.session["user_id"] = user_id 44 | response_str = gt.get_response_str() 45 | self.write(response_str) 46 | 47 | class PcValidateHandler(SessionBaseHandler): 48 | def post(self): 49 | gt = GeetestLib(pc_geetest_id, pc_geetest_key) 50 | challenge = self.get_argument(gt.FN_CHALLENGE, "") 51 | validate = self.get_argument(gt.FN_VALIDATE, "") 52 | seccode = self.get_argument(gt.FN_SECCODE, "") 53 | status = self.session[gt.GT_STATUS_SESSION_KEY] 54 | user_id = self.session["user_id"] 55 | if status: 56 | result = gt.success_validate(challenge, validate, seccode, user_id) 57 | else: 58 | result = gt.failback_validate(challenge, validate, seccode) 59 | self.session["user_id"] = user_id 60 | result = "

登录成功

" if result else "

登录失败

" 61 | self.write(result) 62 | 63 | 64 | class PcAjaxValidateHandler(SessionBaseHandler): 65 | def post(self): 66 | gt = GeetestLib(pc_geetest_id, pc_geetest_key) 67 | challenge = self.get_argument(gt.FN_CHALLENGE, "") 68 | validate = self.get_argument(gt.FN_VALIDATE, "") 69 | seccode = self.get_argument(gt.FN_SECCODE, "") 70 | status = self.session[gt.GT_STATUS_SESSION_KEY] 71 | user_id = self.session["user_id"] 72 | if status: 73 | result = gt.success_validate(challenge, validate, seccode, user_id) 74 | else: 75 | result = gt.failback_validate(challenge, validate, seccode) 76 | self.session["user_id"] = user_id 77 | result = result = {"status":"success"} if result else {"status":"fail"} 78 | self.write(json.dumps(result)) 79 | 80 | class MobileAjaxValidateHandler(SessionBaseHandler): 81 | def post(self): 82 | gt = GeetestLib(mobile_geetest_id, mobile_geetest_key) 83 | challenge = self.get_argument(gt.FN_CHALLENGE, "") 84 | validate = self.get_argument(gt.FN_VALIDATE, "") 85 | seccode = self.get_argument(gt.FN_SECCODE, "") 86 | status = self.session[gt.GT_STATUS_SESSION_KEY] 87 | user_id = self.session["user_id"] 88 | if status: 89 | result = gt.success_validate(challenge, validate, seccode, user_id) 90 | else: 91 | result = gt.failback_validate(challenge, validate, seccode) 92 | self.session["user_id"] = user_id 93 | result = result = {"status":"success"} if result else {"status":"fail"} 94 | self.write(json.dumps(result)) 95 | 96 | if __name__ == "__main__": 97 | app = tornado.web.Application([ 98 | (r"/", MainHandler), 99 | (r"/static/gt.js",JsHandler), 100 | (r"/pc-geetest/register", PcGetCaptchaHandler), 101 | (r"/mobile-geetest/register",MobileGetCaptchaHandler), 102 | (r"/pc-geetest/validate", PcValidateHandler), 103 | (r"/pc-geetest/ajax_validate", PcAjaxValidateHandler), 104 | (r"/mobile-geetest/ajax_validate",MobileAjaxValidateHandler) 105 | ], debug=True) 106 | 107 | app.listen(8088) 108 | tornado.ioloop.IOLoop.instance().start() 109 | -------------------------------------------------------------------------------- /demo/tornado_demo/static/gt.js: -------------------------------------------------------------------------------- 1 | /* initGeetest 1.0.0 2 | * 用于加载id对应的验证码库,并支持宕机模式 3 | * 暴露 initGeetest 进行验证码的初始化 4 | * 一般不需要用户进行修改 5 | */ 6 | (function (global, factory) { 7 | "use strict"; 8 | if (typeof module === "object" && typeof module.exports === "object") { 9 | // CommonJS 10 | module.exports = global.document ? 11 | factory(global, true) : 12 | function (w) { 13 | if (!w.document) { 14 | throw new Error("Geetest requires a window with a document"); 15 | } 16 | return factory(w); 17 | }; 18 | } else { 19 | factory(global); 20 | } 21 | })(typeof window !== "undefined" ? window : this, function (window, noGlobal) { 22 | "use strict"; 23 | if (typeof window === 'undefined') { 24 | throw new Error('Geetest requires browser environment'); 25 | } 26 | var document = window.document; 27 | var Math = window.Math; 28 | var head = document.getElementsByTagName("head")[0]; 29 | 30 | function _Object(obj) { 31 | this._obj = obj; 32 | } 33 | 34 | _Object.prototype = { 35 | _each: function (process) { 36 | var _obj = this._obj; 37 | for (var k in _obj) { 38 | if (_obj.hasOwnProperty(k)) { 39 | process(k, _obj[k]); 40 | } 41 | } 42 | return this; 43 | } 44 | }; 45 | function Config(config) { 46 | var self = this; 47 | new _Object(config)._each(function (key, value) { 48 | self[key] = value; 49 | }); 50 | } 51 | 52 | Config.prototype = { 53 | api_server: 'api.geetest.com', 54 | protocol: 'http://', 55 | type_path: '/gettype.php', 56 | fallback_config: { 57 | slide: { 58 | static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"], 59 | type: 'slide', 60 | slide: '/static/js/geetest.0.0.0.js' 61 | }, 62 | fullpage: { 63 | static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"], 64 | type: 'fullpage', 65 | fullpage: '/static/js/fullpage.0.0.0.js' 66 | } 67 | }, 68 | _get_fallback_config: function () { 69 | var self = this; 70 | if (isString(self.type)) { 71 | return self.fallback_config[self.type]; 72 | } else if (self.new_captcha) { 73 | return self.fallback_config.fullpage; 74 | } else { 75 | return self.fallback_config.slide; 76 | } 77 | }, 78 | _extend: function (obj) { 79 | var self = this; 80 | new _Object(obj)._each(function (key, value) { 81 | self[key] = value; 82 | }) 83 | } 84 | }; 85 | var isNumber = function (value) { 86 | return (typeof value === 'number'); 87 | }; 88 | var isString = function (value) { 89 | return (typeof value === 'string'); 90 | }; 91 | var isBoolean = function (value) { 92 | return (typeof value === 'boolean'); 93 | }; 94 | var isObject = function (value) { 95 | return (typeof value === 'object' && value !== null); 96 | }; 97 | var isFunction = function (value) { 98 | return (typeof value === 'function'); 99 | }; 100 | var callbacks = {}; 101 | var status = {}; 102 | var random = function () { 103 | return parseInt(Math.random() * 10000) + (new Date()).valueOf(); 104 | }; 105 | var loadScript = function (url, cb) { 106 | var script = document.createElement("script"); 107 | script.charset = "UTF-8"; 108 | script.async = true; 109 | script.onerror = function () { 110 | cb(true); 111 | }; 112 | var loaded = false; 113 | script.onload = script.onreadystatechange = function () { 114 | if (!loaded && 115 | (!script.readyState || 116 | "loaded" === script.readyState || 117 | "complete" === script.readyState)) { 118 | 119 | loaded = true; 120 | setTimeout(function () { 121 | cb(false); 122 | }, 0); 123 | } 124 | }; 125 | script.src = url; 126 | head.appendChild(script); 127 | }; 128 | var normalizeDomain = function (domain) { 129 | return domain.replace(/^https?:\/\/|\/$/g, ''); 130 | }; 131 | var normalizePath = function (path) { 132 | path = path.replace(/\/+/g, '/'); 133 | if (path.indexOf('/') !== 0) { 134 | path = '/' + path; 135 | } 136 | return path; 137 | }; 138 | var normalizeQuery = function (query) { 139 | if (!query) { 140 | return ''; 141 | } 142 | var q = '?'; 143 | new _Object(query)._each(function (key, value) { 144 | if (isString(value) || isNumber(value) || isBoolean(value)) { 145 | q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&'; 146 | } 147 | }); 148 | if (q === '?') { 149 | q = ''; 150 | } 151 | return q.replace(/&$/, ''); 152 | }; 153 | var makeURL = function (protocol, domain, path, query) { 154 | domain = normalizeDomain(domain); 155 | 156 | var url = normalizePath(path) + normalizeQuery(query); 157 | if (domain) { 158 | url = protocol + domain + url; 159 | } 160 | 161 | return url; 162 | }; 163 | var load = function (protocol, domains, path, query, cb) { 164 | var tryRequest = function (at) { 165 | 166 | var url = makeURL(protocol, domains[at], path, query); 167 | loadScript(url, function (err) { 168 | if (err) { 169 | if (at >= domains.length - 1) { 170 | cb(true); 171 | } else { 172 | tryRequest(at + 1); 173 | } 174 | } else { 175 | cb(false); 176 | } 177 | }); 178 | }; 179 | tryRequest(0); 180 | }; 181 | var jsonp = function (domains, path, config, callback) { 182 | if (isObject(config.getLib)) { 183 | config._extend(config.getLib); 184 | callback(config); 185 | return; 186 | } 187 | if (config.offline) { 188 | callback(config._get_fallback_config()); 189 | return; 190 | } 191 | var cb = "geetest_" + random(); 192 | window[cb] = function (data) { 193 | if (data.status === 'success') { 194 | callback(data.data); 195 | } else if (!data.status) { 196 | callback(data); 197 | } else { 198 | callback(config._get_fallback_config()); 199 | } 200 | window[cb] = undefined; 201 | try { 202 | delete window[cb]; 203 | } catch (e) { 204 | } 205 | }; 206 | load(config.protocol, domains, path, { 207 | gt: config.gt, 208 | callback: cb 209 | }, function (err) { 210 | if (err) { 211 | callback(config._get_fallback_config()); 212 | } 213 | }); 214 | }; 215 | var throwError = function (errorType, config) { 216 | var errors = { 217 | networkError: '网络错误' 218 | }; 219 | if (typeof config.onError === 'function') { 220 | config.onError(errors[errorType]); 221 | } else { 222 | throw new Error(errors[errorType]); 223 | } 224 | }; 225 | var detect = function () { 226 | return !!window.Geetest; 227 | }; 228 | if (detect()) { 229 | status.slide = "loaded"; 230 | } 231 | var initGeetest = function (userConfig, callback) { 232 | var config = new Config(userConfig); 233 | if (userConfig.https) { 234 | config.protocol = 'https://'; 235 | } else if (!userConfig.protocol) { 236 | config.protocol = window.location.protocol + '//'; 237 | } 238 | jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) { 239 | var type = newConfig.type; 240 | var init = function () { 241 | config._extend(newConfig); 242 | callback(new window.Geetest(config)); 243 | }; 244 | callbacks[type] = callbacks[type] || []; 245 | var s = status[type] || 'init'; 246 | if (s === 'init') { 247 | status[type] = 'loading'; 248 | callbacks[type].push(init); 249 | load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) { 250 | if (err) { 251 | status[type] = 'fail'; 252 | throwError('networkError', config); 253 | } else { 254 | status[type] = 'loaded'; 255 | var cbs = callbacks[type]; 256 | for (var i = 0, len = cbs.length; i < len; i = i + 1) { 257 | var cb = cbs[i]; 258 | if (isFunction(cb)) { 259 | cb(); 260 | } 261 | } 262 | callbacks[type] = []; 263 | } 264 | }); 265 | } else if (s === "loaded") { 266 | init(); 267 | } else if (s === "fail") { 268 | throwError('networkError', config); 269 | } else if (s === "loading") { 270 | callbacks[type].push(init); 271 | } 272 | }); 273 | }; 274 | window.initGeetest = initGeetest; 275 | return initGeetest; 276 | }); 277 | 278 | -------------------------------------------------------------------------------- /demo/tornado_demo/static/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | gt-python-tornado-demo 7 | 65 | 66 | 67 |

极验验证SDKDemo

68 |

69 |
70 |

71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 98 | 99 | 150 |

151 |
152 |

153 | 173 | 174 | 212 |

213 |
214 |

215 | 232 | 233 | 288 | 289 | 290 | 291 | -------------------------------------------------------------------------------- /geetest/__init__.py: -------------------------------------------------------------------------------- 1 | from .geetest import GeetestLib, VERSION -------------------------------------------------------------------------------- /geetest/geetest.py: -------------------------------------------------------------------------------- 1 | #!coding:utf8 2 | import sys 3 | import random 4 | import json 5 | import requests 6 | import time 7 | from hashlib import md5 8 | 9 | 10 | if sys.version_info >= (3,): 11 | xrange = range 12 | 13 | VERSION = "3.2.0" 14 | 15 | 16 | class GeetestLib(object): 17 | 18 | FN_CHALLENGE = "geetest_challenge" 19 | FN_VALIDATE = "geetest_validate" 20 | FN_SECCODE = "geetest_seccode" 21 | 22 | GT_STATUS_SESSION_KEY = "gt_server_status" 23 | 24 | API_URL = "http://api.geetest.com" 25 | REGISTER_HANDLER = "/register.php" 26 | VALIDATE_HANDLER = "/validate.php" 27 | 28 | def __init__(self, captcha_id, private_key): 29 | self.private_key = private_key 30 | self.captcha_id = captcha_id 31 | self.sdk_version = VERSION 32 | self._response_str = "" 33 | 34 | def pre_process(self, user_id=None): 35 | """ 36 | 验证初始化预处理. 37 | """ 38 | status, challenge = self._register(user_id) 39 | self._response_str = self._make_response_format(status, challenge) 40 | return status 41 | 42 | def _register(self, user_id=None): 43 | challenge = self._register_challenge(user_id) 44 | if len(challenge) == 32: 45 | challenge = self._md5_encode("".join([challenge, self.private_key])) 46 | return 1, challenge 47 | else: 48 | return 0, self._make_fail_challenge() 49 | 50 | def get_response_str(self): 51 | return self._response_str 52 | 53 | def _make_fail_challenge(self): 54 | rnd1 = random.randint(0, 99) 55 | rnd2 = random.randint(0, 99) 56 | md5_str1 = self._md5_encode(str(rnd1)) 57 | md5_str2 = self._md5_encode(str(rnd2)) 58 | challenge = md5_str1 + md5_str2[0:2] 59 | return challenge 60 | 61 | def _make_response_format(self, success=1, challenge=None): 62 | if not challenge: 63 | challenge = self._make_fail_challenge() 64 | string_format = json.dumps( 65 | {'success': success, 'gt':self.captcha_id, 'challenge': challenge}) 66 | return string_format 67 | 68 | def _register_challenge(self, user_id=None): 69 | if user_id: 70 | register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}".format( 71 | api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id) 72 | else: 73 | register_url = "{api_url}{handler}?gt={captcha_ID}".format( 74 | api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id) 75 | try: 76 | response = requests.get(register_url, timeout=2) 77 | if response.status_code == requests.codes.ok: 78 | res_string = response.text 79 | else: 80 | res_string = "" 81 | except: 82 | res_string = "" 83 | return res_string 84 | 85 | def success_validate(self, challenge, validate, seccode, user_id=None,gt=None,data='',userinfo=''): 86 | """ 87 | 正常模式的二次验证方式.向geetest server 请求验证结果. 88 | """ 89 | if not self._check_para(challenge, validate, seccode): 90 | return 0 91 | if not self._check_result(challenge, validate): 92 | return 0 93 | validate_url = "{api_url}{handler}".format( 94 | api_url=self.API_URL, handler=self.VALIDATE_HANDLER) 95 | query = { 96 | "seccode": seccode, 97 | "sdk": ''.join( ["python_",self.sdk_version]), 98 | "user_id": user_id, 99 | "data":data, 100 | "timestamp":time.time(), 101 | "challenge":challenge, 102 | "userinfo":userinfo, 103 | "captchaid":gt 104 | } 105 | backinfo = self._post_values(validate_url, query) 106 | if backinfo == self._md5_encode(seccode): 107 | return 1 108 | else: 109 | return 0 110 | 111 | def _post_values(self, apiserver, data): 112 | response = requests.post(apiserver, data) 113 | return response.text 114 | 115 | def _check_result(self, origin, validate): 116 | encodeStr = self._md5_encode(self.private_key + "geetest" + origin) 117 | if validate == encodeStr: 118 | return True 119 | else: 120 | return False 121 | 122 | def failback_validate(self, challenge, validate, seccode): 123 | """ 124 | failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果. 125 | """ 126 | if not self._check_para(challenge, validate, seccode): 127 | return 0 128 | validate_str = validate.split('_') 129 | encode_ans = validate_str[0] 130 | encode_fbii = validate_str[1] 131 | encode_igi = validate_str[2] 132 | decode_ans = self._decode_response(challenge, encode_ans) 133 | decode_fbii = self._decode_response(challenge, encode_fbii) 134 | decode_igi = self._decode_response(challenge, encode_igi) 135 | validate_result = self._validate_fail_image( 136 | decode_ans, decode_fbii, decode_igi) 137 | return validate_result 138 | 139 | def _check_para(self, challenge, validate, seccode): 140 | return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip())) 141 | 142 | def _validate_fail_image(self, ans, full_bg_index , img_grp_index): 143 | thread = 3 144 | full_bg_name = str(self._md5_encode(str(full_bg_index)))[0:10] 145 | bg_name = str(self._md5_encode(str(img_grp_index)))[10:20] 146 | answer_decode = "" 147 | for i in range(0,9): 148 | if i % 2 == 0: 149 | answer_decode += full_bg_name[i] 150 | elif i % 2 == 1: 151 | answer_decode += bg_name[i] 152 | x_decode = answer_decode[4:] 153 | x_int = int(x_decode, 16) 154 | result = x_int % 200 155 | if result < 40: 156 | result = 40 157 | if abs(ans - result) < thread: 158 | return 1 159 | else: 160 | return 0 161 | 162 | def _md5_encode(self, values): 163 | if type(values) == str: 164 | values = values.encode() 165 | m = md5(values) 166 | return m.hexdigest() 167 | 168 | def _decode_rand_base(self, challenge): 169 | str_base = challenge[32:] 170 | i = 0 171 | temp_array = [] 172 | for i in xrange(len(str_base)): 173 | temp_char = str_base[i] 174 | temp_ascii = ord(temp_char) 175 | result = temp_ascii - 87 if temp_ascii > 57 else temp_ascii - 48 176 | temp_array.append(result) 177 | decode_res = temp_array[0]*36 + temp_array[1] 178 | return decode_res 179 | 180 | def _decode_response(self, challenge, userresponse): 181 | if len(userresponse) > 100: 182 | return 0 183 | shuzi = (1, 2, 5, 10, 50) 184 | chongfu = set() 185 | key = {} 186 | count = 0 187 | for i in challenge: 188 | if i in chongfu: 189 | continue 190 | else: 191 | value = shuzi[count % 5] 192 | chongfu.add(i) 193 | count += 1 194 | key.update({i: value}) 195 | res = 0 196 | for i in userresponse: 197 | res += key.get(i, 0) 198 | res = res - self._decode_rand_base(challenge) 199 | return res 200 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.6.0 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!coding:utf8 2 | import sys 3 | 4 | try: 5 | from setuptools import setup 6 | except: 7 | from distutils.core import setup 8 | VERSION = "3.2.0" 9 | 10 | 11 | if __name__ == "__main__": 12 | with open('requirements.txt') as f: 13 | required = f.read().splitlines() 14 | setup( 15 | name="geetest", 16 | version=VERSION, 17 | packages=['geetest'], 18 | url='http://github.com/GeeTeam/gt-python-sdk', 19 | license='', 20 | author='Geetest', 21 | author_email='admin@geetest.com', 22 | description='Geetest Python SDK', 23 | install_requires=required, 24 | ) 25 | --------------------------------------------------------------------------------