flask实例----爱家租房网(二)

四。图片验证码和短信验证码

在注册页面生成一个图片验证码,在获取手机验证码之前验证。在创建验证码视图之前,先额外创建一个文件用于存放系统中的常量。如(ihome/constants.py)

"""保存常量的文件"""

# 图片验证码的redis有效期
IMAGE_CODE_REDIS_EXPIRES = 300

# 短信验证码的redis有效期
SMS_CODE_REDIS_EXPIRES = 300

# 发送短信验证码的间隔
SEND_SMS_CODE_INTERVAL = 60

这里有一套验证码生成器的包captcha,以及各错误提示文件response_code.py。存于ihome/utils内
链接:https://pan.baidu.com/s/1jjJHQ0tEM03z5_EfuHLUsg
提取码:sizh
链接:https://pan.baidu.com/s/1rF0aTaP-Jo2fTGa3XXArzw
提取码:sia8


生成图片验证码的流程图:① 浏览器生成一个特定编号(用于数据库内存放image_code_id)。② 发起图片验证码获取的请求。③ 服务器收到请求,生成图片验证码,并将验证码的真是值和编号存放redis数据库。④ 返回验证码给浏览器。

短信验证码生成的过程:① 浏览器向服务器发送获取短信验证码的请求(携带参数有用户填写的验证码值和图片验证码编号)。 ② 服务器接收请求,比较输入值的正确值,如果正确就发送验证码。

在这里插入图片描述

图片验证码

接口文件verify_code.py

  1. 从url获取图片验证码id (过)
  2. 校验参数 (过)
  3. 业务逻辑
    3.1 生成图片验证码 ------- captcha.generate_captcha()
    3.2 尝试将图片验证码的真实值保存到redis
  4. 返回应答(图片验证码) ----添加头部信息标识为图片格式 resp.headers[“Content-Type”] = “image/jpg”
from flask import current_app, jsonify, make_response
from . import api
from ihome.utils.captcha.captcha import captcha 
from ihome import redis_store
from ihome.constants import IMAGE_CODE_REDIS_EXPIRES
from ihome.utils.response_code import RET

# GET 127.0.0.1:5000/api/V1.0/image_code/<image_code_id>
@api.route("/image_code/<image_code_id>")
def get_image_code(image_code_id):
    """获取图片验证码, 返回验证码图片"""
    # 1. 获取参数  url中已经获取 

    # 2. 校验参数   参数存在,无其他校验

    # 3. 业务逻辑   
    # 3.1 生成验证码图片
    # 名字  真实文本  图片数据
    name, text, image_data = captcha.generate_captcha()
    # 3.2 将图片和编号存储到redis
    # redis_store.set("image_code_%s" % image_code_id, text)
    # redis_store.expire("image_code_%s" % image_code_id, IMAGE_CODE_REDIS_EXPIRES)
    try:        # 防止redis连接中断
        redis_store.setex("image_code_%s" % image_code_id, 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")

    # 4. 返回图片
    resp = make_response(image_data)
    resp.headers["Content-Type"] = "image/jpg"
    return resp

前端js:(register.js

  1. 声明一个环境变量imageCodeId=‘’ 用于保存图片验证码编号
  2. 生成随机数的函数 generateUUID(),用来作为图片验证码编号
  3. 定义获取图片验证码的函数(刷新页面和点击验证码式执行)
    3.1 生成图片的验证码编号
    3.2 凭借图片的url,并使用js添加到<img src=’…’>
//  保存图片验证码编号
var imageCodeId = "";

// 生成随机数,用于生成image_code_id
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;
}

// 获取验证码图片
function generateImageCode() {
    // 形成图片验证码的后端地址,设置到页面中,让浏览器请求图片
    // 1. 生成图片验证码的编号, imageCodeId有定义全局变量
    imageCodeId = generateUUID();
    // 2. 指示图片url
    var url="/api/v1.0/image_code/" + imageCodeId
    $(".image-code img").attr("src", url)
}

然后,别忘了在版本的初始化文件_init_.py导入视图。最后,我们就可以在127.0.0.1:5000/register.html查看到我们需要的验证码图片了。

短信验证

要实现发送短信,需要引入第三方服务——容联云云通讯( www.yuntongxun.com
可以在官网地址下载python的sdk和对应的demo
https://www.yuntongxun.com/doc/ready/demo/1_4_1_2.html

官网上面没有跟新python3.x版本的sdk,所以需要我们手动更改sdk。以下是已经更改好的sdk文件和测试文件
链接:https://pan.baidu.com/s/1ZPJz8zoh7YtlQsF-S6jwdA
提取码:iyv0

短信验证码接口:

  1. 获取参数(手机号,图片验证码值,图片验证码编号)
  2. 校验参数(仅参数完整)
  3. 业务逻辑
    3.1 获取redis数据库中存储的图片验证码诊值
    3.2 图片验证码仅可使用一次,删除图片验证码的redis
    3.3 对比图片验证码
    3.4 判断用户是否在60s内有过操作
    3.5 判断手机号是否已经注册
    3.6 如果未注册,生成手机验证码并保存到redis,并且添加用户记录(防止60s内连续操作)
    3.7 发送短信
  4. 返回应答 (在3.7成功时,已经返回)
from flask import current_app, jsonify, make_response, request
from . import api
from ihome.utils.captcha.captcha import captcha 
from ihome import redis_store, db
from ihome.constants import IMAGE_CODE_REDIS_EXPIRES, SMS_CODE_REDIS_EXPIRES, SEND_SMS_CODE_INTERVAL
from ihome.utils.response_code import RET
from ihome.models import User
import random
from ihome.libs.yuntongxun.SendTemplateSMS import CCP

# 获取手机验证码
# GET 127.0.0.1:5000/api/V1.0/sms_code/<mobile>?image_code=xxxxxx&image_code_id=xxxxxx
@api.route("/sms_code/<re(r'1[34578]\d{9}'):mobile>")
def get_sms_code(mobile):
    # 1. 获取参数
    image_code = request.args.get("image_code")
    image_code_id = request.args.get("image_code_id")
    print(image_code)

    # 2. 校验参数
    if not all([image_code, image_code_id]):
        # 参数不完整
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # 3. 业务逻辑
    # 3.1 获取redis中存储的图片验证码真值
    try:
        real_image_code = redis_store.get("image_code_%s" % image_code_id).decode("utf-8")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="redis获取图片验证码失败")
    if real_image_code is None:
        # 如果图片验证码过期
        return jsonify(errno=RET.NODATA, errmsg="图片验证码失效")

    # 3.2 图片验证码仅可使用一次,删除图片验证码
    try:
        redis_store.delete("image_code_%s" % image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        
    # 3.2 对比验证码
    if real_image_code.upper() != image_code.upper():
        # 填写错误
        return jsonify(errno=RET.DATAERR, errmsg="图片验证码错误") 

    # 3.4 判断该手机号是否在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 == 1:
            # 操作频繁
            return jsonify(errno=RET.REQERR, errmsg="请求过于频繁,请60s后重试")
    # 3.5 判断手机号是否已注册
    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="用户已存在")
    # 3.6 如果未注册,生成短信验证码并保存,发送短信验证码
    sms_code = "%06d" % random.randint(0, 999999)
    try:
        redis_store.setex("sms_code_%s" % mobile, SMS_CODE_REDIS_EXPIRES, sms_code)
        # 保存发送给这个号码的记录,防止用户在60s内再次重复发送
        redis_store.setex("send_sms_code_%s" % mobile, SEND_SMS_CODE_INTERVAL, 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码异常")
    # 3.7 发送短信
    ccp = CCP()
    try:
        result = ccp.send_template_sms(mobile, [sms_code, int(SMS_CODE_REDIS_EXPIRES/60)], 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="第三方发送短信异常")
    if result == 0:
        # 发送成功
        return jsonify(errno=RET.OK, errmsg="发送成功")
    else:
        return jsonify(errno=RET.THIRDERR, errmsg="发送失败")

    # 4. 返回值

前端js:(register.js
点击发送验证码执行的函数sendSMSCode()

  1. 取消按钮的onclick功能,如果有值填写错误,显示错误信息,并且回复按钮的onclick事件
  2. 构建请求(携带参数图片验证码和图片验证码编号)
  3. ajax向后端发送get请求,如果成功,创建60s定时器,取消按钮的onclick事件,如果失败回复按钮的onclick事件。
// 点击发送验证码的时候执行
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: "image_code_"+imageCodeId    // 图片验证码编号 (全局变量有定义)
    }
    // 向后端发送请求
    $.get("/api/v1.0/sms_code/"+mobile, reg_data, function(resp){
        if (resp.errno == "0"){
            // 发送成功
            var num = 60;
            // 创建定时器60s,定时“获取验证码”按钮
            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);
            $(".phonecode-a").attr("onclick", "sendSMSCode();");
        }
    });
}

展开阅读全文
©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读