DAY6 图形验证码以及短信验证码的处理


 

到我们点击项目首页的注册时,会弹出一个注册页面,里面需要我们后台提供图形验证码以及手机验证码

 

 

 下面我们来依次处理

图形验证码的处理

我们先来简单的做个分析

首先当我们点击注册时,我们需要给到通过浏览器给服务器发送一个随机码来进行下次请求时双方的校验,用浏览器的Javascript生UUID来解决

,通过GET请求发送(通过两个方面来考虑,第一个这个本身不需要加密,第二个是通过img标签里src默认为GET)

当我们给服务器发送了UUID之后,服务器对UUID进行保存,然后校验UUID,然后生成图片验证码(图片验证码分为三个部分,生成的图片验证码名字,图片里的验证码文本,画上了验证码的图片),我们需要把UUID保存在redis中,且作为保存值的键,然后提取图片验证码的验证码文本作为保存的值。然后我们再给浏览器返回画上了验证码的图片就可以了。这样当用户通过返回的图形验证码图片里的文字输入了文本验证码后再次给到服务器,这里浏览器还需要将UUID和输入文本的

文本验证码一起给到服务器。因为服务器需要通过给到的UUID作为取值的键来取上次保存的数据,也是做进一步的校验和实现状态保存,你是上次给我UUID的那个浏览器。只有当两次请求给到服务器的UUID一致时才能够进行上次生成图片验证码文本的提取

大致分析完之后我们就开始写代码

浏览器前端的处理:

我们来到我们首页的HTML文件,打开我们项目下的index.html,然后找到我们图形验证码的相关内容,

最初文件的img标签, src 里面有一堆数据,我们先复制一下最初的img标签,复制了然后注释掉,然后再进行删除

 

 

然后Ctrl加左键点击函数来到函数的内容页面,这里面就有我们图片验证码的相关内容:

 

 

 

 

 最初的文件里function generateImageCode() {}里面是空的,这里我把代码补充了,代码我也复制再下面并做个简单的解释;

function generateImageCode() {
        imageCodeId = generateUUID() #生成UUID,文件的最下面有生成UUID的函数,这里只是函数的调用和用新的变量来对imageCOdeId变量进行覆盖
    var url = "/passport/imageCode?imageCodeId="+ imageCodeId  #自定义一个URL地址的变量,后台先创建对应的视图,然后再填写对应的视图URL加上?kv
                                      将UUID通过GET请求给到服务器后台 $(".get_pic_code").attr("src",url) #将构造好的URL给到url }

 

文件后面生成UUID的函数

 

 

 

 后台创造新的视图函数:

首先在我们的moduls文件夹里面再创建一个新的Python文件夹(passport)用来存放我们登录注册相关的相关内容,然后建立一个叫views 的Python文件来存放相关的视图函数

 

 

 然后就是在__init__里面写入蓝图三步,这里我们需要在实例化的时候加入url_prefix,也就是前缀,一定要记得在前缀最开始加上反斜杠/

 

 

 完成之后再在我们项目的info的__init__文件夹,然后导入注册蓝图

 

 

 完成之后来到我们的视图函数页面进行视图函数的编写,这里先简单的编写一下路由和函数就可以了,后面我们再进行补全,这里主要是为了补全我们前端的URL地址:

 

 

 

当我们完成对前端文件的修改后,再次执行主文件并点击主页面的注册,然后后端查看请求就可以看到我们的UUID 了,当点击图片验证码时会刷新请求出现携带不同的UUID的请求 :

 

 

 这样我们图形验证码前端HTML文件的修改就完成了

 

后端代码的处理(对应视图函数的编写):

当我们去写项目的视图函数时需要在我们函数的文本注释里面写上我们的思路以及相应的实现步骤,这样方便我们后续代码的编写,这是十分重要的。

 

 

 我简单的分析一下图片里的步骤,并对没一步 的代码进行一个拆分

我们需要接收UUID来作为保存图片验证码 的值以及实现第二次交互时的状态保持,也就是用户填写好验证码之后进行校验,这就是第一步:

接收参数需要在flask里面导入request

 # 1.接受参数(imageCodeId) 
    imageCodeId = request.args.get("imageCodeId") (接收的get请求的数据,post为from)  

对UUID进行简单的校验,判断是否存在这就是第二步:

 # 2.校验参数
    #   2.1校验imageCodeId是否存在 
    if not imageCodeId:   #通过取反来判断为空,并进行自定义异常的抛出
        abort(403)

 

抛出自定义异常需要flask里面导入abort

 

 

  

然后生成我们的图片验证码,这一步需要第三工具包来实现:

首先在我们的info文件夹里建立一个我们存放第三方工具的新文件夹utils,然后将我们生成图片验证码的文件放在里面

 

 

 在生成图片验证码的captcha.py文件里面有测试用例的编写,是可以执行并进行查看是否有对应的生成,是可以执行的,记得执行进行测试确认是否能用。

 

 

 控制台里生成的内容分别为,本次生成的图片验证码编号(也就是名字),本次生成的文字文档,图片的二进制格式

 

 

 无误后在编写视图函数的views文件里面进行导入

 

 

 注意这里的导入的三个captcha 的对应,第一个是captcha是文件夹,第二个是Python文件,第三个是文件里面的实例化变量

 

 

 然后就可以写我们生成的第三步,生成我们的图形验证码,并进行拆包用三个变量来接收我们每一次生成的图片验证码的内容:

# 3.生成图形验证码证码
    name, imageCode, image = captcha.generate_captcha() #这里是对元祖进行了拆分,每个变量存入不同的内容,分别是本次生成的图片验证码编号(也就是名字),
                                  本次生成的文字文档(验证码文本),图片的二进制格式(图片)

完成图片验证码的生成之后就可以进行第四步的处理

首先导入我们创建好的连接redis数据库的变量,以及存放着我们数据设置的文件 constants,它里面有我们对数据的一些设置

 

 

 

 

 

 然后用我们的数据库变量来进行第四步的操作

    try: #因为存在连接不上数据库的问题所以需要先进行异常捕捉并进行处理
        redis_store.set("imageCode"+imageCodeId,imageCode,ex=constants.IMAGE_CODE_REDIS_EXPIRES) #ex设置的是数据保存的有效时间
                            #键进行了我们接收的UUID值和前缀的拼接,值为生成图片验证码 的文本验证码
                                                                                                                                                                                                                               
except Exception as e :              
current_app.logger.error(e)           #这里需要从falsk导入current_app来写入本次异常信息并进行自定义异常的处理
abort(500)

完成之后我们完成最后一步返回结果,也就是给浏览器发送我们本次的二进制图片,并交给前端进行渲染

res=make_response(image)           #这里我们需要对返回相应的相应头Content-Type进行处理,不然我们在页面查看源代码会是乱码,这里我们也需要从flask导入make_response
res.headers["Content-Type"] = "image/jpg" return res 

完成之后我们图片验证码 的生成以及后续通过对比校验用户的填写是否正确就完成了,打开主页面的注册就会显示我们的图片验证码每次刷新也会不同:

 完整的代码:

@passport_blue.route("/imageCode",methods=["GET"])
def image_code():
    """
    提供图片验证码
    1.接受参数 (imageCodeId)
    2.校验参数
        2.1 校验imageCodeId是否存在
    3.生成图形验证码
    4.将文字的图形验证码保存到redis
    5.返回结果
    :return: 
    """
    # 1.接受参数(imageCodeId)
    imageCodeId = request.args.get("imageCodeId")
    # 2.校验参数
    #   2.1校验imageCodeId是否存在
    if not imageCodeId:
        abort(403)
    # 3.生成图形验证码证码把存到redis
    name, imageCode, image = captcha.generate_captcha()
    print(imageCode)
    # 4.将文字的图形验证码保存到rides
    try:
        redis_store.set("imageCode"+imageCodeId,imageCode,ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e :
        current_app.logger.error(e)
        abort(500)

    # 5.返回结果
    res=make_response(image)
    res.headers["Content-Type"] = "image/jpg"
    return res

  

 

 短信验证码的处理

短信验证码的处理需要通过下图中的接口设计来实现,因为我们需要通过第三方给用户发送短信,所以有一个规范。

 

 

 

前端代码的处理

首先我们还是先来处理前端的HTML,首先找到短信验证码的相关代码

 

 

 然后Ctrl加鼠标左键点击进入函数的文件后找到手机验证码对应的函数,然后进行修改,下面是手机验证码的函数代码

 

 

// 发送短信验证码
function sendSMSCode() {
    // 校验参数,保证输入框有数据填写
    $(".get_code").removeAttr("onclick");
    var mobile = $("#register_mobile").val();
    if (!mobile) {
        $("#register-mobile-err").html("请填写正确的手机号!");
        $("#register-mobile-err").show();
        $(".get_code").attr("onclick", "sendSMSCode();");
        return;
    }
    var imageCode = $("#imagecode").val();
    if (!imageCode) {
        $("#image-code-err").html("请填写验证码!");
        $("#image-code-err").show();
        $(".get_code").attr("onclick", "sendSMSCode();");
        return;
    }

    var params = {
        'mobile':mobile,  #用户填写的手机号,需要给到服务器
        'image_code':imageCode,   # 用户填写的短信验证码,需要给到服务器   
        'image_code_id':imageCodeId   #用户本次生成图形验证码的UUID  
    };

    // TODO 发送短信验证码
    $.ajax({                                      #规范的接口设计,包括请求地址以及方式和参数
        url:'/passport/sms_code',   // 请求地址
        type:'post',                // 请求方法
        data:JSON.stringify(params),// 请求参数
        contentType:'application/json',// 数据类型
        success:function (response) {  // 回调函数
            if (response.errno == '0') {
                // 发送短信验证码成功
                alert(response.errmsg);
            } else {
                alert(response.errmsg);
            }
        }
    });
}

 

前端页面代码的修改完成之后,编写后端的代码。

后端代码的处理(对应视图函数的编写):

和图片验证码一样,我们需要在passport文件夹的views文件里写入对应的视图函数,也就是写在我们图片验证码的后面。 

 

 

 

首先写入对应的视图函数名和URL地址 ,这里我们规定了只接收post请求,这是按照一开始的接口规范来设置的。

 

 

 

 完成之后我们也需要先写入注释文档,说明函数的作用以及分析思路并写入实现的步骤顺序。

首先该函数的作用为:注册时短信验证码的发送

然后先做个简单的分析

第一步肯定还是接收参数,这次我们根据接口设计的图片得知我们有三个参数需要接收

1,用户填写的手机号

2,用户填写的短信验证码

3,浏览器生成的UUID (这个是用来校验图形验证码)

第二步校验参数,首先检查是否传全,然后分别检验

第三步当数据全且无误时,在后台生成我们的4位数的短信验证码

第四步 将验证码保存到我们的数据库中

第五步通过第三方给用户发送我们后天生成的验证码

第六步返回结果

 """                                            #函数的注释文档
        注册时短信发送
    1. 接收参数(mobile,image_code,image_code_id)
    2. 校验参数
        2.1 判断参数是否齐全(mobile,image_code,image_code_id)
        2.2 校验手机号是否正确,正则
        2.3 查看redis 是否有image_code_id
            否:返回json数据
        2.4 校验用户填写的验证码是否正确
        否:返回json数据
    3.生成短信验证码
    4.将短信验证码保存到redis
    5.发送手机短信
        否:返回json数据
    6.返回结果
    :return: 
    """

下面我们还是通过编写每一步的代码来实现整个短信验证码的功能

第一步

#   1. 接收参数(mobile,image_code,image_code_id)
    mobile=request.json.get("mobile")      #接收json数据对应的参数
    image_code=request.json.get("image_code")
    image_code_id=request.json.get("image_code_id")

所有接收的变量的命名是通过最开始的端口设计规范来写的,以及规定了发送的是json数据,所以我们对json 数据进行接收.request.json,这里还是用了flask的request包

第二步

#   2. 校验参数
# 2.1 判断参数是否齐全(mobile,image_code,image_code_id)
if not all([mobile,image_code,image_code_id]):
return jsonify(errno=RET.PARAMERR,errmsg="参数缺失")
# 2.2 校验手机号是否正确,正则
mobile=mobile.strip() #去空
if not re.findall("^1[3|4|5|7|8][0-9]{9}$",mobile):
return jsonify(errno=RET.PARAMERR,errmsg="手机号格式有误")
# 2.3 查看redis 是否有image_code_id
try:                        #对数据库连接进行异常捕捉
image_code_server = redis_store.get("imageCode" + image_code_id)
except Exception as e :
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="数据库连接失败")
# 否:返回json数据
if not image_code_server: #没有找到对应键值对json数据的返回
return jsonify(errno=RET.NODATA,errmsg="未找到该图片验证码")
# 2.4 校验用户填写的验证码是否正确
if image_code_server.strip().lower() != image_code.strip().lower():  #去空strip,小写lower
return jsonify(errno=RET.PARAMERR,errmsg="图片验证码填写错误")
# 否:返回json数据

首先判断所有参数是否齐全,通过if not 取反即可,然后是通过手机号的正则写法来判断手机号是否正确,这里返回json数据也是通过最开始的接口设计,

通过flask导入jsonify就可以给前端返回json数据  

 

 

通过规范我们得知需要返回两个参数,分别是 errno(错误码),errmsg(错误信息) ,错误信息有我们自己填写字符串来进行本次错误的描述,而错误码在我们之前复制

到项目的response_code.py的文件中

 

 

 它里面有一个记录了errno的类RET,以及每个类属性的中文注释

 

 

 

 我们将它导入到我们的视图文件views中,然后通过调用类属性的方法也就是对象.的形式来进行相关的调用

 

 当我们进行第三方操作时,就需要进行异常捕捉,数据库有时候会连接不上

当我们查看redis 是否有image_code_id,也就是redis保存的文字验证码是否存在,这里需要先查看所UUID的值拼接上

前缀所产生的键是否存在,当和最开始拼接的保存文字验证码的键一致时,就可以取出里面的文字验证码来对用户输入的

文字验证码进行校验,所以首先我们先判断键值对是否存在,然后再通过取出值来判断用户输入的文字验证码是否正确,也就是

image_code.这里我们为了用户体验进行了字符串去空和小写的设置,这一步也是很重要的,手机号也进行了去空。这样我们接收的三个数据

也就分别校验完毕了

第三步

3.生成短信验证码
    smsCode="%04d" %random.randint(0, 9999)

 这一步通过了导入random随机数包来生成最大位数为4位的随机数,然后通过格式化输出中不满足位数自动补0的方法来进行完成验证码的生成。

第四步

 

#     4.将短信验证码保存到redis #对数据库连接异常进行捕捉
    try:
        redis_store.set("SMScode:"+mobile,smsCode,ex=constants.SMS_CODE_REDIS_EXPIRES) #ex 设置有效时长
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg="数据库连接失败")
 

  

 当我们使用第三方工具时都需要进行异常捕捉,因为我们不确定我们与第三方之间的连接是否正常,问题随时都有可能发生,所以进行异常捕捉的处理

这里我们也对键进行了拼接,前缀+用户的手机号,然后设置了保存的有效时长,方法同保存的图片验证验证码时长一样,然后对错误json数据的返回

第五步

 

#     5.发送手机短信
#     try:
#         sms_res = CCP().send_template_sms(mobile, [smsCode, constants.SMS_CODE_REDIS_EXPIRES / 60], 1)  #复制测试时文件的代码,进行相关的修改
#     except Exception as e :
#             current_app.logger.error(e)
#             return jsonify(errno=RET.THIRDERR,errmsg="发送短信失败")
    sms_res = 0
    if sms_res != 0 :
        return jsonify(errno=RET.THIRDERR,errmsg="发送短信失败")
#         否:返回json数据
if sms_res != 0 :                           #通过查看元文件代码可以发现发送成功会返回结果0,
                           失败为-1,所以还需要对结果进行校验,这里的问题有可能是第三方不能给用户发送短信所造成的

return jsonify(errno=RET.THIRDERR,errmsg="发送短信失败")

 

这里需要导入我们第二个第三方工具包

首先在info里面再创建一个新的文件夹libs来存放第三方平台 

libs用来存放第三方平台,然后utils用来存放第三方文件

 

 

 将我们的第三方平台的文件夹复制到里面,还是先进入到我们要使用的sms.py里面执行进行测试

 

 

 

 如果成功后就导入到我们views文件里面

 

 

 

 这里我们导入的是大写的CCP,一个没有实例化的类,然后进行手机短信的发送和相关异常的代码的处理。

过来就行这样手机验证码的处理就完成了

 

 

 第六步

 # 6.返回结果
    return jsonify(errno=RET.OK,errmsg="发送成功") 

 

这里也需要按照格式返回json数据,这里返回的结果为正常的errno,也就是OK。

这样短信验证码的处理就完成了,但是还有两个问题需要解决

发送短信验证码中问题的解决

CRSF防护

 

 

 

 

当我们开启了CRSF防护时,每次浏览器给服务器发送post请求时,也就是比较私密的请求数据时就会提示,这里我们只是先将CRSF防护关闭,

到了项目的最后来进行统一的处理

所以第一个问题CRSF防护问题的解决就是先关掉它,在info的__init__文件里里注释掉

 

 

 第二个问题是当我们通过DEBUG模式打断点接收数据会发现,我们保存到redis的文本验证码数据类型和用户输入的文本验证码数据类型会不一样,两个

不同的数据类型的变量是没有办法进行对比的,我们需要改变数据类型来让它们保持一致并进行比较,

 

 我们接收到的用户输入的验证码数据类型为str,而我们之前保持到redis当中的文本验证码数据类型为字节,在redis的保存中,一般保存数据为二进制,所以我们

需要对保存在redis中的文本验证码的数据类型进行设置,让它以文本类型进行保存,这样方便我们做对比

首先我们创建的redis连接对象的变量代码,再最后加上一个参数,它会将返回的数据类型进行转码。设置为TRUE就行

decode_responses=True

 

 

 完整的代码为

redis_store=StrictRedis(host=configs[config_name].REDIS_HOST,port=configs[config_name].REDIS_PORT,decode_responses=True)

 设置完成后重启就可以了,这样数据类型就一样了。

 

 

 还有可以问题为第三方台

当我们使用第三方平台时需要注册并设置可以接收短信的手机号,最开始我们只能设置三个,也就是有三个用户可以完成注册,这样是不行的,所以我们需要将我们的第五步注释掉

并使它返回的结果为0,我们可以直接通过后台拿到我们生成好的验证码直接复制到注册页面来进行后续用户的注册操作

 

 

 放几张第三方平台数据的截图和使用(通过sms.py文件):

 

 

 

 

 完整的代码:

@passport_blue.route("/sms_code",methods=["POST"])
def sms_code():
    """
        注册时短信发送
    1. 接收参数(mobile,image_code,image_code_id)
    2. 校验参数
        2.1 判断参数是否齐全(mobile,image_code,image_code_id)
        2.2 校验手机号是否正确,正则
        2.3 查看redis 是否有image_code_id
            否:返回json数据
        2.4 校验用户填写的验证码是否正确
        否:返回json数据
    3.生成短信验证码
    4.将短信验证码保存到redis
    5.发送手机短信
        否:返回json数据
    6.返回结果
    :return: 
    """
#   1. 接收参数(mobile,image_code,image_code_id)
    mobile=request.json.get("mobile")
    image_code=request.json.get("image_code")
    image_code_id=request.json.get("image_code_id")
#   2. 校验参数
#   2.1 判断参数是否齐全(mobile,image_code,image_code_id)
    if not all([mobile,image_code,image_code_id]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数缺失")
#   2.2 校验手机号是否正确,正则
    mobile=mobile.strip()
    if not re.findall("^1[3|4|5|7|8][0-9]{9}$",mobile):
        return jsonify(errno=RET.PARAMERR,errmsg="手机号格式有误")
#   2.3 查看redis 是否有image_code_id
    try:
        image_code_server = redis_store.get("imageCode" + image_code_id)
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg="数据库连接失败")
#             否:返回json数据
    if not image_code_server:
        return jsonify(errno=RET.NODATA,errmsg="未找到该图片验证码")
#         2.4 校验用户填写的验证码是否正确
    if image_code_server.strip().lower() != image_code.strip().lower():
        return jsonify(errno=RET.PARAMERR,errmsg="图片验证码填写错误")
#         否:返回json数据
#     3.生成短信验证码
    smsCode="%04d" %random.randint(0, 9999)
    print(smsCode)
#     4.将短信验证码保存到redis
    try:
        redis_store.set("SMScode:"+mobile,smsCode,ex=constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e :
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg="数据库连接失败")
     # 5.发送手机短信
    # try:
    #     sms_res = CCP().send_template_sms(mobile, [smsCode, constants.SMS_CODE_REDIS_EXPIRES / 60], 1)
    # except Exception as e :
    #         current_app.logger.error(e)
    #         return jsonify(errno=RET.THIRDERR,errmsg="发送短信失败")
    sms_res = 0
    if sms_res != 0 :
        return jsonify(errno=RET.THIRDERR,errmsg="发送短信失败")
#         否:返回json数据
    # 6.返回结果
    return jsonify(errno=RET.OK,errmsg="发送成功")

  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM