圖片驗證碼和簡訊驗證碼開發
阿新 • • 發佈:2018-11-11
圖片驗證碼和簡訊驗證碼開發
tip :前後端分離,先開發後端,後完善前端
一、圖片驗證碼流程
1、引入captcha包放入utils
不是獨立的第三方包放入utils,獨立的包放入libs裡面
- captcha.py 裡的生成驗證碼方法
captcha.generate_captcha()
- response_code.py 是各種的錯誤返回說明
- commons.py 是定義的正則表達轉換器
from werkzeug.routing import BaseConverter # 定義正則轉換器 class ReConverter(BaseConverter): """""" def __init__(self, url_map, regex): # 呼叫父類的初始化方法 super(ReConverter, self).__init__(url_map) # 儲存正則表示式 self.regex = regex
2.定義驗證碼api路由 verify.py
GET 127.0.0.1/api/v1.0/image_codes/<image_code_id>
, 儲存到redis 資料庫,但是redis資料型別 redis: 字串 列表 雜湊 set
的鍵值對,不能用列表,[列表裡只能是字串,不能放{鍵值對}],雜湊可以用,但是使用雜湊維護有效期的時候只能整體設定,想要單條維護因此選用字串。連線redis儲存資料和設定有效期redis_store.setex("image_code_%s" % image_code_id, 180, text)
其中180s 是常量,可以放置在一個單獨的constants.py
constants.py
# coding:utf-8
# 圖片驗證碼的redis有效期, 單位:秒
IMAGE_CODE_REDIS_EXPIRES = 180
# 簡訊驗證碼的有效期
SMS_CODE_REDIS_EXPIRES = 300
# 簡訊驗證碼的間隔
SEND_SMS_CODE_INYERVAL = 6
verify.py # GET 127.0.0.1/api/v1.0/image_codes/<image_code_id> @api.route("/image_codes/<image_code_id>") def get_image_code(image_code_id): """ 獲取圖片驗證碼 : params image_code_id: 圖片驗證碼編號 :return: 正常:驗證碼圖片 異常:返回json """ # 業務邏輯處理 # 生成驗證碼圖片 # 名字,真實文字, 圖片資料 name, text, image_data = captcha.generate_captcha() # 將驗證碼真實值與編號儲存到redis中, 設定有效期 # redis: 字串 列表 雜湊 set # "key": xxx # 使用雜湊維護有效期的時候只能整體設定 # "image_codes": {"id1":"abc", "":"", "":""} 雜湊 hset("image_codes", "id1", "abc") hget("image_codes", "id1") # 單條維護記錄,選用字串 # "image_code_編號1": "真實值" # "image_code_編號2": "真實值" # redis_store.set("image_code_%s" % image_code_id, text) # redis_store.expire("image_code_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES) # 記錄名字 有效期 記錄值 try: redis_store.setex("image_code_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text) except Exception as e: # 記錄日誌 current_app.logger.error(e) # return jsonify(errno=RET.DBERR, errmsg="save image code id failed") return jsonify(errno=RET.DBERR, errmsg="儲存圖片驗證碼失敗") # 返回圖片 resp = make_response(image_data) resp.headers["Content-Type"] = "image/jpg" return resp
3.前端的完善 register.js + register.html
前端生成圖片驗證碼的編號UUID 函式register.js
register.js
// 儲存圖片驗證碼編號
// 定義的全域性變數,編號後邊會使用
var imageCodeId = "";
function generateUUID() {
var d = new Date().getTime();
if(window.performance && typeof window.performance.now === "function"){
d += performance.now(); //use high-precision timer if available
}
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
}
形成圖片驗證碼的後端地址, 設定到頁面中,讓瀏覽請求驗證碼圖片
register.js
function generateImageCode() {
// 形成圖片驗證碼的後端地址, 設定到頁面中,讓瀏覽請求驗證碼圖片
// 1. 生成圖片驗證碼編號
imageCodeId = generateUUID();
// 是指圖片url
var url = "/api/v1.0/image_codes/" + imageCodeId;
$(".image-code img").attr("src", url);
}
register.html 連線
<input type="text" class="form-control" name="imagecode" id="imagecode" placeholder="圖片驗證碼" required>
<div class="input-group-addon image-code" onclick="generateImageCode();"><img src=""></div>
二、簡訊驗證碼
1、 get 請求 api/v1.0/sms_codes/
- 要先驗證圖片的驗證碼的編號和驗證碼是否完整,
獲取引數 前端頁面使用者填寫的圖片驗證碼 + 前端生成 編號
校驗引數 引數是否完整
從redis資料庫取出真實驗證碼 real_image_code 網路連線有可能出現異常,丟擲資料庫異常
判斷驗證碼是否過期 只要判斷redis的真實的驗證碼是否為None - 及時刪除redis的圖片驗證碼,防止使用者使用同一個圖片驗證碼多次登陸手機號
比較驗證碼的值 real_image_code.lower() != image_code.lower() - 判斷手機號是否發過簡訊驗證碼 設定send_flag 獲取redis的標誌,並在前端
.js
裡定義60s 後才可以操作
判斷手機號是否存在 匯入db ,models, 用User.query.filter_by().first() 是否為空,is not None 手機號存在 - 隨機數用格式化字元
%06d,
手機號不存在,則生成簡訊驗證碼 - 儲存簡訊驗證碼+驗證碼的send標識 redis_store.setex() 連線資料庫的要丟擲異常
- 傳送簡訊 第三方連線可能出現錯誤,丟擲異常
- 返回值判斷
# get 請求 api/v1.0/sms_codes/<mobile>
@api.route("/sms_codes/<re(r'1[34578]\d{9}'):mobile>")
def get_sms_code(mobile):
# 獲取引數
image_code = request.args.get("image_code")
image_code_id = request.args.get("image_code_id")
# 校驗引數
if not all([image_code_id, image_code]):
return jsonify(errno=RET.PARAMERR, errmsg="引數不完整")
# 業務邏輯處理
# 1. 從redis 取出真實驗證碼
try:
real_image_code = redis_store.get("image_code_%s" % image_code_id)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DATAERR, errmsg="資料庫異常")
# 2.判斷是否過期
if real_image_code is None:
return jsonify(errno=RET.NODATA, errmsg="驗證碼過期")
# 刪除redis 中的圖片驗證碼 ,防止使用者使用同一個圖片驗證碼多次
try :
redis_store.delete("image_code_%s " % image_code_id)
except Exception as e:
current_app.logger.error(e)
# 3. 比較驗證碼的值
if real_image_code.lower() != image_code.lower():
return jsonify(errno=RET.DATAERR, errmsg="圖片驗證碼錯誤")
# 手機號是否處理過,對於這個手機號的操做在60s之內是否有記錄
try:
send_flag = redis_store.get("send_sms_code_%s " % mobile)
except Exception as e:
current_app.logger.error(e)
else:
if send_flag is not None:
return jsonify(errno=RET.REQERR, errmsg="請求過於頻繁,請在60 s之後再操作")
# 4. 判斷手機號是否存在
try:
user = User.query.filter_by(mobile=mobile).first()
except Exception as e:
current_app.logger.error(e)
else:
if user is not None:
return jsonify(errno=RET.DATAEXIST, errmsg="手機號已存在")
# 5. 隨機數用格式化字元,手機號不存在,則生成簡訊驗證碼
sms_code = "%06d" % random.randint(0, 999999)
# ^6. 儲存簡訊驗證碼
try:
redis_store.setex("sms_code_%s" % mobile, constants.SMS_CODE_REDIS_EXPIRES,sms_code)
redis_store.setex("send_sms_code_%s" % mobile,constants.SEND_SMS_CODE_INYERVAL, 1)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DATAERR, errmsg="儲存驗證碼錯誤")
# 7.傳送簡訊
try:
ccp = CCP()
result = ccp.send_template_sms(mobile,[sms_code,int(constants.SMS_CODE_REDIS_EXPIRES/60)],1)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.THIRDERR, errmsg="失敗")
#返回值
print(result)
if result == 0:
return jsonify(errno= RET.OK, errmsg= "成功")
else:
return jsonify(errno= RET.THIRDERR,errmsg ="傳送失敗")
2、前端的完善
向後端構造請求的引數 向後端傳送請求,resp 是後端返回的響應值,因為後端返回的時json字串
//register.js
function sendSMSCode() {
$(".phonecode-a").removeAttr("onclick");
var mobile = $("#mobile").val();
if (!mobile) {
$("#mobile-err span").html("請填寫正確的手機號!");
$("#mobile-err").show();
$(".phonecode-a").attr("onclick", "sendSMSCode();");
return;
}
var imageCode = $("#imagecode").val();
if (!imageCode) {
$("#image-code-err span").html("請填寫驗證碼!");
$("#image-code-err").show();
$(".phonecode-a").attr("onclick", "sendSMSCode();");
return;
}
// 向後端構造請求的引數
var reg_data = {
image_code: imageCode ,
image_code_id: imageCodeId,
};
// 像後端傳送請求
$.get("/api/v1.0/sms_codes/"+ mobile,reg_data,function(resp){
//resp 是後端返回的響應值,因為後端返回的時json字串
// 所以ajax幫助我們把這個json字串轉換為js物件,resp是轉換後的物件
if (resp.errno =="0"){
var num = 60;
var timer = setInterval(function(){
if (num > 1){
$(".phonecode-a").html(num + "秒");
num -= 1
} else {
$(".phonecode-a").html("獲取驗證碼");
$("..phonecode-a").attr("onclick","sendSMSCode();");
clearInterval(timer)
}
}, 1000, 60)
} else {
alert(resp.errmsg);
}
} );
}