您当前的位置: 首页 > 

IT之一小佬

暂无认证

  • 1浏览

    0关注

    1192博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

美多商城之验证码(短信验证码2)

IT之一小佬 发布时间:2021-06-28 21:50:52 ,浏览量:1

二、短信验证码 2.3 短信验证码后端逻辑 1. 短信验证码接口设计

1.请求方式

选项方案请求方法GET请求地址/sms_codes/(?P1[3-9]\d{9})/

2.请求参数:路径参数和查询字符串

参数名类型是否必传说明mobilestring是手机号image_codestring是图形验证码uuidstring是唯一编号

3.响应结果:JSON

字段说明code状态码errmsg错误信息 2. 短信验证码接口定义
class SMSCodeView(View):
    """短信验证码"""

    def get(self, reqeust, mobile):
        """
        :param reqeust: 请求对象
        :param mobile: 手机号
        :return: JSON
        """
        pass
3.路由设置
from django.conf.urls import url
from . import views


urlpatterns = [
    #  图形验证码
    url(r'^image_codes/(?P[\w-]+)/$', views.ImageCodeView.as_view()),
    #  短信验证码
    url(r'sms_codes/(?P1[3-9]\d{9})/$', views.SMSCodeView.as_view())
]
4. 短信验证码后端逻辑实现
from meiduo_mall.meiduo_mall.utils.response_code import RETCODE
import logging
import random
from meiduo_mall.apps.verifications.lib.yuntongxun.ccp_sms import CCP


#  创建日志输出器
logger = logging.getLogger('django')


class SMSCodeView(View):
    """短信验证码"""

    def get(self, reqeust, mobile):
        """
        :param reqeust: 请求对象
        :param mobile: 手机号
        :return: JSON
        """
        # 接收参数
        image_code_client = reqeust.GET.get('image_code')
        uuid = reqeust.GET.get('uuid')

        # 校验参数
        if not all([image_code_client, uuid]):
            return http.JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数'})

        # 创建连接到redis的对象
        redis_conn = get_redis_connection('verify_code')
        # 提取图形验证码
        image_code_server = redis_conn.get('img_%s' % uuid)
        if image_code_server is None:
            # 图形验证码过期或者不存在
            return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效'})
        # 删除图形验证码,避免恶意测试图形验证码
        try:
            redis_conn.delete('img_%s' % uuid)
        except Exception as e:
            logger.error(e)
        # 对比图形验证码
        image_code_server = image_code_server.decode()  # bytes转字符串
        if image_code_client.lower() != image_code_server.lower():  # 转小写后比较
            return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})

        # 生成短信验证码:生成6位数验证码
        sms_code = '%06d' % random.randint(0, 999999)
        logger.info(sms_code)
        # 保存短信验证码
        redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        # 发送短信验证码
        CCP().send_template_sms(mobile,[sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)

        # 响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})

2.4 短信验证码前端逻辑 1. Vue绑定短信验证码界面

1.register.html

  • 短信验证码: [[ sms_code_tip ]] [[ error_sms_code_message ]]
  • 2.register.js

    check_sms_code(){
        if(this.sms_code.length != 6){
            this.error_sms_code_message = '请填写短信验证码';
            this.error_sms_code = true;
        } else {
            this.error_sms_code = false;
        }
    },
    
    2. axios请求短信验证码

    1.发送短信验证码事件处理

    send_sms_code(){
        // 避免重复点击
        if (this.sending_flag == true) {
            return;
        }
        this.sending_flag = true;
    
        // 校验参数
        this.check_mobile();
        this.check_image_code();
        if (this.error_mobile == true || this.error_image_code == true) {
            this.sending_flag = false;
            return;
        }
    
        // 请求短信验证码
        let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code+'&uuid='+ this.uuid;
        axios.get(url, {
            responseType: 'json'
        })
            .then(response => {
                if (response.data.code == '0') {
                    // 倒计时60秒
                    var num = 60;
                    var t = setInterval(() => {
                        if (num == 1) {
                            clearInterval(t);
                            this.sms_code_tip = '获取短信验证码';
                            this.sending_flag = false;
                        } else {
                            num -= 1;
                            // 展示倒计时信息
                            this.sms_code_tip = num + '秒';
                        }
                    }, 1000, 60)
                } else {
                    if (response.data.code == '4001') {
                        this.error_image_code_message = response.data.errmsg;
                        this.error_image_code = true;
                    } else { // 4002
                        this.error_sms_code_message = response.data.errmsg;
                        this.error_sms_code = true;
                    }
                    this.generate_image_code();
                    this.sending_flag = false;
                }
            })
            .catch(error => {
                console.log(error.response);
                this.sending_flag = false;
            })
    },
    

    2.发送短信验证码效果展示

    2.5 补充注册时短信验证逻辑 1. 补充注册时短信验证后端逻辑

    1.接收短信验证码参数

    sms_code_client = request.POST.get('sms_code')
    

    2.保存注册数据之前,对比短信验证码

    redis_conn = get_redis_connection('verify_code')
    sms_code_server = redis_conn.get('sms_%s' % mobile)
    if sms_code_server is None:
        return render(request, 'register.html', {'sms_code_errmsg':'无效的短信验证码'})
    if sms_code_client != sms_code_server.decode():
        return render(request, 'register.html', {'sms_code_errmsg': '输入短信验证码有误'})
    
    2. 补充注册时短信验证前端逻辑

    1.register.html

  • 短信验证码: [[ sms_code_tip ]] [[ error_sms_code_message ]] {% if sms_code_errmsg %} {{ sms_code_errmsg }} {% endif %}
  • 2.6 避免频繁发送短信验证码

    存在的问题:

    • 虽然我们在前端界面做了60秒倒计时功能。
    • 但是恶意用户可以绕过前端界面向后端频繁请求短信验证码。

    解决办法:

    • 在后端也要限制用户请求短信验证码的频率。60秒内只允许一次请求短信验证码。
    • 在Redis数据库中缓存一个数值,有效期设置为60秒。
    1. 避免频繁发送短信验证码逻辑分析

    2. 避免频繁发送短信验证码逻辑实现

    1.提取、校验send_flag

    send_flag = redis_conn.get('send_flag_%s' % mobile)
    if send_flag:
        return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信过于频繁'})
    

    2.重新写入send_flag

    # 保存短信验证码
    redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    # 重新写入send_flag
    redis_conn.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
    

    3.界面渲染频繁发送短信提示信息

    if (response.data.code == '4001') {
        this.error_image_code_message = response.data.errmsg;
        this.error_image_code = true;
    } else { // 4002
        this.error_sms_code_message = response.data.errmsg;
        this.error_sms_code = true;
    }

    2.7 pipeline操作Redis数据库

    Redis的 C - S 架构:

    • 基于客户端-服务端模型以及请求/响应协议的TCP服务。
    • 客户端向服务端发送一个查询请求,并监听Socket返回。
    • 通常是以阻塞模式,等待服务端响应。
    • 服务端处理命令,并将结果返回给客户端。

    存在的问题:

    • 如果Redis服务端需要同时处理多个请求,加上网络延迟,那么服务端利用率不高,效率降低。

    解决的办法:

    • 管道pipeline

    1. pipeline的介绍

    管道pipeline

    • 可以一次性发送多条命令并在执行完后一次性将结果返回。
    • pipeline通过减少客户端与Redis的通信次数来实现降低往返延时时间。

    实现的原理

    • 实现的原理是队列。
    • Client可以将三个命令放到一个tcp报文一起发送。
    • Server则可以将三条命令的处理结果放到一个tcp报文返回。
    • 队列是先进先出,这样就保证数据的顺序性。

    2. pipeline操作Redis数据库

    1.实现步骤

    1. 创建Redis管道
    2. 将Redis请求添加到队列
    3. 执行请求
    

    2.代码实现

    # 创建Redis管道
    pl = redis_conn.pipeline()
    # 将Redis请求添加到队列
    pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    pl.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
    # 执行请求
    pl.execute()

    关注
    打赏
    1665675218
    查看更多评论
    立即登录/注册

    微信扫码登录

    0.4096s