Last active
May 13, 2019 03:41
-
-
Save imwilsonxu/885efcd808e9730a4ccc4a0da2f4f356 to your computer and use it in GitHub Desktop.
[极验验证 + 云片短信 + Flask + WTForms] #flask
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
import re, requests, httplib, urllib, json | |
from flask import Flask, render_template, session, request, jsonify, current_app | |
from flask_wtf import Form | |
from wtforms import Field, StringField, SubmitField | |
from wtforms.validators import DataRequired, Regexp | |
from wtforms.widgets import TextInput, HTMLString | |
from geetest import GeetestLib | |
app = Flask(__name__) | |
app.secret_key = "your_secret_key" | |
GEETEST_ID = "" | |
GEETEST_KEY = "" | |
YUNPIAN_APIKEY = "" | |
YUNPIAN_TPL_ID = "" | |
YUNPIAN_SMS_HOST = "" | |
YUNPIAN_PORT = "" | |
YUNPIAN_PORT_SMS_TPL_SEND_URI = "" | |
class GeeTestWidget(object): | |
def __call__(self, field, **kwargs): | |
html_string = ( | |
u"<div id='embed-captcha'></div>" | |
u"<p id='wait' class='show'>正在加载验证码...</p>" | |
) | |
return HTMLString(html_string) | |
class GeeTestField(Field): | |
widget = GeeTestWidget() | |
class YunpianWidget(TextInput): | |
def __call__(self, field, **kwargs): | |
kwargs.setdefault('id', field.id) | |
kwargs.setdefault('type', self.input_type) | |
return HTMLString( | |
u"<a class='btn btn-default btn-send-sms' href='javascript:void(0)''>获取验证码</a>" | |
'<input %s>' % self.html_params(name=field.name, **kwargs)) | |
class YunPianField(Field): | |
widget = YunpianWidget() | |
class DemoForm(Form): | |
cellphone = StringField(u"手机", | |
[DataRequired(), | |
Regexp(u"^\d{11}$", message="11 digits only.")]) | |
geetest = GeeTestField() | |
sms_code = YunPianField('', | |
[DataRequired(), | |
Regexp(u"^\d{4}$", message="4 digits only.")]) | |
submit = SubmitField() | |
@app.route('/geetest/init') | |
def init_geetest(): | |
"""Init GeeTest captcha""" | |
gt = GeetestLib(GEETEST_ID, GEETEST_KEY) | |
# 判断极验的服务器状态, 并生成好验证初始化的标准串 | |
status = gt.pre_process() | |
session[gt.GT_STATUS_SESSION_KEY] = status | |
# 向极验服务器请求验证结果 | |
response_str = gt.get_response_str() | |
# {"challenge": "xxx", "gt": "xxx", "success": 1} | |
return response_str | |
# https://www.yunpian.com/api/demo.html | |
def send_sms_code(code, mobile): | |
# if current_app.debug: | |
# return | |
params = urllib.urlencode({ | |
'apikey': YUNPIAN_APIKEY, | |
'tpl_id': YUNPIAN_TPL_ID, | |
'tpl_value': urllib.urlencode({'#code#': code}), | |
'mobile': mobile}) | |
headers = { | |
"Content-type": "application/x-www-form-urlencoded", | |
"Accept": "text/plain" | |
} | |
conn = httplib.HTTPSConnection(YUNPIAN_SMS_HOST, | |
port=YUNPIAN_PORT, | |
timeout=30) | |
conn.request("POST", YUNPIAN_PORT_SMS_TPL_SEND_URI, params, headers) | |
response = conn.getresponse() | |
response_str = response.read() | |
conn.close() | |
return json.loads(response_str) | |
@app.route('/sms/send', methods=['POST']) | |
def send_sms(): | |
"""Send SMS via Yunpian""" | |
gt = GeetestLib(GEETEST_ID, GEETEST_KEY) | |
challenge = request.form[gt.FN_CHALLENGE] | |
validate = request.form[gt.FN_VALIDATE] | |
seccode = request.form[gt.FN_SECCODE] | |
status = session[gt.GT_STATUS_SESSION_KEY] | |
passed = False | |
if status: | |
# 向极验服务器请求验证结果 | |
passed = gt.success_validate(challenge, validate, seccode) | |
else: | |
# 极验服务器宕机情况下在本地完成二次验证 | |
passed = gt.failback_validate(challenge, validate, seccode) | |
if not passed: | |
error = { | |
"success": 0, | |
"msg": u"请拖动左边滑块进行验证。", | |
} | |
return jsonify(error) | |
else: | |
cellphone = request.form.get("cellphone") | |
if not cellphone: | |
error = { | |
"success": 0, | |
"msg": u"请输入手机。", | |
} | |
return jsonify(error) | |
rule = re.compile("^\d{11}$") | |
if not re.search(rule, cellphone): | |
error = { | |
"success": 0, | |
"msg": u"手机格式有误。请输入11位数字。", | |
} | |
return jsonify(error) | |
# TODO: Should encrypt and store in database or session. | |
sms_code = "1234" | |
session["sms_code"] = sms_code | |
response_str = send_sms_code(sms_code, cellphone) | |
return jsonify(response_str) | |
@app.route("/demo", methods=['GET', 'POST']) | |
def demo(): | |
form = DemoForm() | |
if form.validate_on_submit(): | |
if not "sms_code" in session or not session["sms_code"] or \ | |
session["sms_code"] != form.sms_code.data: | |
form.sms_code.errors.append(u"验证码有误,请重新获取。") | |
else: | |
return "success!" | |
return render_template('demo.html', form=form) | |
if __name__ == "__main__": | |
app.run(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html class="no-js" lang=""> | |
<head> | |
<meta charset="utf-8"> | |
<title>Demo: 极验验证 + 云片短信 + Flask + WTForms</title> | |
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> | |
</head> | |
<body> | |
<div class="container"> | |
<h1 class="text-center">Demo: 极验验证 + 云片短信 + Flask + WTForms</h1> | |
<hr> | |
<div class="row"> | |
<div class="col-md-4 col-md-offset-4"> | |
<form class="form" action="{{ url_for('demo') }}" method="post"> | |
{{ form.hidden_tag() }} | |
<div class=""> | |
{{ form.cellphone(class_="form-control", placeholder="手机") }} | |
<ul class="field-errors"> | |
{% for error in form.cellphone.errors -%} | |
<li><span class='label label-danger'>{{ error }}</span></li> | |
{%- endfor %} | |
</ul> | |
</div> | |
<div id="geetest"> | |
{{ form.geetest() }} | |
<ul class="field-errors"> | |
{% for error in form.geetest.errors -%} | |
<li><span class='label label-danger'>{{ error }}</span></li> | |
{%- endfor %} | |
</ul> | |
</div> | |
<div> | |
{{ form.sms_code(class_="form-control pull-right", style="display:inline-block;width:200px", placeholder="验证码") }} | |
<ul class="field-errors"> | |
{% for error in form.cellphone.errors -%} | |
<li><span class='label label-danger'>{{ error }}</span></li> | |
{%- endfor %} | |
</ul> | |
</div> | |
{{ form.submit(class_="btn btn-block btn-success") }} | |
</form> | |
</div> | |
</div> | |
</div> | |
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.js"></script> | |
<script src="http://static.geetest.com/static/tools/gt.js"></script> | |
<script type="text/javascript"> | |
$(document).ready(function() { | |
var handlerEmbed = function (captchaObj) { | |
$(".btn-send-sms").click(function (e) { | |
var validate = captchaObj.getValidate(); | |
if (!validate) { | |
$("#geetest ul.field-errors").append("<li>请先拖动验证码到相应位置</li>"); | |
e.preventDefault(); | |
} else { | |
var form = $(this).closest("form"); | |
var cellphone = $("#cellphone").val(); | |
var re = /^\d{11}$/; | |
if (cellphone.match(re)) { | |
$.ajax({ | |
url: "/sms/send?t=" + (new Date()).getTime(), | |
type: "post", | |
dataType: "json", | |
data: { | |
"cellphone": cellphone, | |
"geetest_challenge": form.find(":hidden[name=geetest_challenge]").val(), | |
"geetest_validate": form.find(":hidden[name=geetest_validate]").val(), | |
"geetest_seccode": form.find(":hidden[name=geetest_seccode]").val(), | |
}, | |
success: function(data) { | |
alert(data.msg); | |
}, | |
error: function(data) { | |
alert(data.msg); | |
}, | |
}); | |
} else { | |
$("#cellphone").after("<span class='label label-danger'>请输入正确的手机,11位数字。</span>"); | |
e.preventDefault(); | |
} | |
} | |
}); | |
// 将验证码加到id为captcha的元素里,同时会有三个input的值: geetest_challenge, geetest_validate, geetest_seccode | |
captchaObj.appendTo("#embed-captcha"); | |
captchaObj.onReady(function () { | |
$("#wait")[0].remove(); | |
}); | |
// 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html | |
}; | |
$.ajax({ | |
// 获取id,challenge,success(是否启用failback) | |
url: "/geetest/init?t=" + (new Date()).getTime(), // 加随机数防止缓存 | |
type: "get", | |
dataType: "json", | |
success: function (data) { | |
// 使用initGeetest接口 | |
// 参数1:配置参数 | |
// 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件 | |
initGeetest({ | |
gt: data.gt, | |
challenge: data.challenge, | |
product: "embed", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效 | |
offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注 | |
// 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config | |
}, handlerEmbed); | |
} | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment