本项目使用图片验证码的地方为注册模块中
简单的流程为:
浏览器向服务器发起请求,服务器获取验证码图片与真实值保存到redis中,并将验证码图片返回给浏览器,当获取短信验证码时进行验证码的验证,如果正确,则发送短信验证码。
如果多个用户发送验证码呢?要去怎么找验证码?怎么验证?
这个时候,在发送短信验证码的同时携带图片验证码的参数:
那么图片验证码的编号就应该是浏览器在发起图片验证码请求时就创建好了编号,一起发送过去,服务器生成图片验证码,将验证码的真实值以及编号一同保存到redis中,当发起短信验证码验证时,服务器就可以在redis中取出编号所对应的的图片验证码的真实值进行比较。流程更新为:
采用RESTful API风格
将captcha(别人写好的生成验证码的工具)包放到utils目录下
将response_code.py(自定义的状态码与返回值)放到utils目录下
在api_1_0目录下创建一个验证码的verify_code.py文件,管理图片以及短信验证码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import current_app, jsonify, make_response
from . import api
from ihome.utils.captcha.captcha import captcha
from ihome import redis_store, constants
from ihome.utils.response_code import RET
# GET 127.0.0.1:5000/image_codes/
@api.route('/image_codes/')
def get_image_code(image_code_id):
"""
获取验证码图片
:param image_code_id: 图片验证码编号
:return: 如果出现异常,返回异常信息,否则,返回验证码图片
"""
# 生成验证码图片
# 名字,真是文本,图片数据
name, text, image_code = captcha.generate_captcha()
# 将编号以及验证码的真实值保存到redis(选择字符串)中,并设置有效期(自定义有效期为180秒,设置成了常量,在constants中)
# redis_store.set('iamge_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 failed")
# 出现异常,返回json格式的提示
return jsonify(errno=RET.DBERR, errmsg="保存图片验证码失败")
# 没有异常 返回验证码图片,并指定一下Content-Type(默认为test/html)类型为image,不改不认识图片
resp = make_response(image_code)
resp.headers['Content-Type'] = 'image/jpg'
return resp
在api目录下的init.py文件中导入verify_code
from . import demo, verify_code
启动项目,浏览器输入 "127.0.0.1:5000/api/v1.0/image_codes/123" ,其中123是任意写的编号,这里只是测试用的
redis数据库中:
注意:captcha这个文件是基于python2.7写的,当前是python3.7,所以直接拿过来是不行的,需要改一些地方
1. xrange在python3.x版本中取消了,代替的是range,但是range返回的是列表,而xrange返回的是一个生成器,因此,为了尽可能少的改动原来的代码,我自定义写了一个xrange,直接import过来即可
2. 在python2.7版本中的string下的uppercare和lowercase已经在python3.x版本中改掉了,在这里需要手动改一下,ascii_uppercare,ascii_lowercase
3. cStringIO在python3.x版本中也取消了,可以用 io 模块下的 BytesIO 代替
第9行 from io improt BytesIO
第16行 from ihome.utils.commons import xrange
第73、210行 string.uppercase ----> string.ascii_uppercase
string.lowercase ----> string.ascii_lowercase
第212行 out = BytesIO()
# ihome/utils/commons.py
# 正则转换器
class ReConverter(BaseConverter):
...
# xrange
def xrange(start, end=None, step=1):
if end == None:
end = start
start = 0
if step > 0:
while start < end:
yield start
start += step
elif step < 0:
while start > end:
yield start
start += step
else:
return 'step can not be zero'
设置一下前端的图片验证码,使其正常加载
在浏览器访问注册页面时,应自动发送图片验证码的请求,其次,点击验证码图片则再次发起请求
在js文件中完成该项功能:
// 保存图片验证码编号
var imageCodeId = "";
function generateImageCode() {
// 形成图片验证码的后端地址, 设置到页面中,让浏览请求验证码图片
// 1. 生成图片验证码编号
imageCodeId = generateUUID();
// 设置图片url
var url = "/api/v1.0/image_codes/" + imageCodeId;
$(".image-code img").attr("src", url);
}
..
$(document).ready(function() {
generateImageCode();
$("#mobile").focus(function(){
$("#mobile-err").hide();
});
$("#imagecode").focus(function(){
$("#image-code-err").hide();
});
....
});
清除一下浏览器的缓存,并访问注册页面 "127.0.0.1:5000/register.html"
验证在下一节中提及