Skip to content

Instantly share code, notes, and snippets.

@imwilsonxu
Last active May 13, 2019 03:41
Show Gist options
  • Save imwilsonxu/885efcd808e9730a4ccc4a0da2f4f356 to your computer and use it in GitHub Desktop.
Save imwilsonxu/885efcd808e9730a4ccc4a0da2f4f356 to your computer and use it in GitHub Desktop.
[极验验证 + 云片短信 + Flask + WTForms] #flask
# -*- 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)
<!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