美文网首页
Django项目用户注册功能(三)

Django项目用户注册功能(三)

作者: 违心唯心 | 来源:发表于2019-08-10 12:31 被阅读0次

    一、获取短信验证码

    1.业务流程分析

    • 校验手机号码
    • 校验图形验证码
    • 校验有效时间内有送记录
    • 生成短信验证码
    • 发送短信
    • 保存短信验证码(redis)
    • 保存发送记录

    2、接口设计

    2.1 接口说明
    GET POST 其实功能上没什么区别,一般情况下:
    GET,是为了获取资源,查询资源数据
    POST,是生成或者新建数据,数据相对安全

    条目 说明
    请求方法 POST
    url定义 /sms_code/
    参数格式 表单(form)

    2.2 参数说明

    参数名字 类型 是否必须 描述
    mobile 字符串 用户输入的手机号码
    captcha 字符串 用户输入的图形验证码

    2.3 返回的结果:
    返回的结果:

    {
          "errno":"0",
          "errmsg":"验证码发送成功",
    }
    

    3、后端代码(一)

    由于至二级写校验手机号码图形验证码会让代码看起来不太爽,所以在 verification 里面新建 forms.py ,先放一放,先写完views

    #由于要进行表单验证,此时应导入表单模块
    from django import forms
    
    #检验图形验证码
    class CheckImageForm(forms.Form):
        '''
        1 校验手机号码
        2 校验图形验证码
        3 校验有效时间内有送记录
        '''
        pass
    

    verification_views:

    #由于要写类视图,这行必须要写进去
    from django.views import View
    
    from utils.res_code import json_response,Code,error_map
    from .forms import CheckImageForm
    
    class SmsCodeView(View):
        '''
        发送短信验证码,
        url:/sms_code/
        '''
        def post(self,request):
            '''
              4 生成短信验证码
              5 发送短信
              6 保存短信验证码(redis)
              7 保存发送记录
            :param request:
            :return:
            '''
            form = CheckImageForm(request.POST)
            if form.is_valid():
                pass
            else:
                #将失败信息进行拼接
                #定义一个空列表,接收错误信息
                err_msg_list = []
                #将失败信息迭代放进空列表
                for item in form.errors.values():
                    #item 也是一个列表,所以把错误信息放在item的第一位
                    err_msg_list.append(item[0])
                    #错误提示的拼接
                err_msg_str = '/'.join(err_msg_list)
                return json_response(errno=Code.PARAMERR,errmsg=err_msg_str)
    

    然后切换至前端先验证功能是否能正常使用~~

    4、前端代码(一)

        //5.发送短信验证码
         let $smsBurtton = $('.sms-captcha');
         $smsBurtton.click(()=>{
            //首先拿到图形验证码
            let sCaptcha = $('input[name="captcha_graph"]').val();
            if (sCaptcha === ''){
                message.showError('孩子 请输入图形验证码!!');
                return
            }
            //判断手机号码是否准备好
            if (!isMobileReady){
                fnCheckMobile();
                return
            }
            //若都校验好了则发送ajax
            $
                .ajax({
                    url:'/sms_code/',
                    type:'POST',
                    data:{
                        mobile:$mobile.val(), //注意这些值是要找到你自己设置的变量
                        captcha:sCaptcha,
                    },
                    dataType:'json',
                })
                // 相当于success:function(res){}
                .done((res)=>{
                    if (res.errno !== '0'){
                        message.showError(res.errmsg)
                    }else {
                        message.showSuccess(res.errmsg)
                    }
                })
                .fail(()=>{
                    message.showError('服务器请求超时,请重试!')
                });
    

    此时进行测试,你会发现会有403错误出现,也就是传说中的,为了防止跨域攻击

    01 csrf 403.jpg

    解决方法:
    约定俗成,在 /static/js/common.js 里面加上下面代码

    /*=== get cookie start ===*/
     function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie !== '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = cookies[i].trim();
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) === (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }
    /*=== get cookie end ===*/
    
    /*=== csrf start ===*/
        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
                }
            }
        });
    /*=== csrf end  ===*/
    

    详解django文档

    你以为这样就完了吗? 错!!还需要在 POST的页面,在表单里面加上 {% csrf_token %}

    5.后端代码(二)

    在敲了这么久之后,前端验证的第一部分算是基本上完成,剩下的稍后再完善,此时再切换回后端代码........

    首先在之前落下的 forms.py 里面创建 mobile 、captcha与前端字段对应

    from django import forms
    
    #检验图形验证码
    class CheckImageForm(forms.Form):
        '''
        1 校验手机号码
        2 校验图形验证码
        3 校验有效时间内有送记录
        '''
        #定义mobile与前端对应
        mobile = forms.CharField(max_length=11,min_length=11,error_messages={
            'max_length':'手机长度有误,请重新输入',
            'min_length':'手机长度有误,请重新输入',
            'required':'手机号码不能为空!!',
        })
        # 定义captcha与前端对应
        captcha = forms.CharField(max_length=4,min_length=4,error_messages={
            'max_length': '图形验码度有误,请重新输入',
            'min_length': '图形验码长度有误,请重新输入',
            'required': '图形验码不能为空!!',
        })
    

    然后再 contants.py里面声明 短信验证码存时间,与发送间隔

    #短信验证码长度
    SMS_CODE_LENGTH = 4
    #短信验证码发送间隔 单位秒
    SMS_CODE_INTERVAL = 60
    #短信验证码过期时间 单位分钟
    SMS_CODE_EXPIRES = 5
    

    然后回到视图 views.py 里面补回成功信息的流程

     if form.is_valid():
                #获取手机号码
                mobile = form.cleaned_data.get('mobile')
                #生成验证码
                sms_code = ''.join([random.choice('0123456789') for _ in range(constants.SMS_CODE_LENGTH)])
                #发送验证码
                #保存发送记录
                logger.info('发送短信验证码[正常][mobile: %s  sms_code: %s ]'%(mobile,sms_code))
                #保存验证码
                #创建短信验证码发送记录的key
                sms_flags_key = 'sms_flags_%s'%(mobile)
                #创建短信验证码内容的key
                sms_text_key = 'sms_text_%s'%(sms_code)
                #创建redis数据库连接
                redis_conn = get_redis_connection(alias='verify_code')
                #创建管道
                pl = redis_conn.pipeline()
                try:
                    # pl.setex(key,时间,内容)
                    pl.setex(sms_flags_key,constants.SMS_CODE_INTERVAL,1)
                    pl.setex(sms_text_key,constants.SMS_CODE_EXPIRES*60,sms_code)
                    #让管道通知redis执行命令
                    pl.execute()
                    return json_response(errmsg='短信验证码发送成功,请留意短信')
                except Exception as e:
                    logger.error('redis 执行异常:%s'%(e))
                    return  json_response(errno=Code.UNKOWNERR,errmsg=error_map[Code.UNKOWNERR])
    
    03 settings.jpg

    6.表单校验

    在forms.py里面新增手机号码正则校验器

    from django.core.validators import RegexValidator
    #创建手机号码正则校验器
    #mobile_validaor = RegexValidator(正则表达式,错误信息)
    mobile_validaor = RegexValidator(r'^1[3-9]\d{9}$','手机号码格式有误!')
    
    • 校验图形验证
    • 检验是否60s内发送过验证码
    • 校验手机号码
        def clean(self):
            clean_data = super().clean()
            mobile = clean_data.get('mobile')
            captcha = clean_data.get('captcha')
            if mobile and captcha :  # 如果前面的检验有问题就不需要往下执行了
               #1.校验图形验证码
                #将session中的验证码与用户输入的对比
                #因为没有request参数,所以复写__init__
                image_code = self.request.session.get('image_code') # 要与views.image_code_view 的字段一致
                if  not image_code:
                    raise forms.ValidationError('图形验证码失效')
                if image_code.lower() != captcha.lower():
                    raise forms.ValidationError('验证码错误,请重新输入')
                #2.是否60s内发送过验证码
                #存在于redis
                redis_conn = get_redis_connection(alias='verify_code')
                if redis_conn.get('sms_flags_%s'%(mobile)):
                    raise forms.ValidationError('发送短信验证码过于频繁')
                #3.校验手机号码是否注册
                if User.objects.filter(mobile=mobile).count():
                     raise forms.ValidationError('手机号码已注册,请换一个手机号码')
    
            return clean_data
    

    7、最后加上前端倒计时

              let $smsBurtton = $('.sms-captcha');
        $smsBurtton.click(() => {
            //首先拿到图形验证码
            let sCaptcha = $('input[name="captcha_graph"]').val();
            if (sCaptcha === '') {
                message.showError('孩子 请输入图形验证码!!');
                return
            }
            //判断手机号码是否准备好
            if (!isMobileReady) {
                fnCheckMobile();
                return
            }
            //若都校验好了则发送ajax
            $
                .ajax({
                    url: '/sms_code/',
                    type: 'POST',
                    data: {
                        mobile: $mobile.val(),
                        captcha: sCaptcha,
                    },
                    dataType: 'json',
                })
                // 相当于success:function(res){}
    
                            .done((res) => {
                    if (res.errno !== '0') {
                        message.showError(res.errmsg)
                    } else {
                        message.showSuccess(res.errmsg);
                        //设置禁用按钮
                        $smsBurtton.attr('disabled',true);
                        //倒计时
                        var a = 60;
                        function set_time() {
                            if (a == 1) {
                                clearInterval(timer2);
                                //document.getElementById("sms-captcha").innerHTML = "获取短信验证码";
                                $smsBurtton.html("获取短信验证码");
                                $smsBurtton.removeAttr('disabled');
                            } else {
                                a--;
                                $smsBurtton.html (a+'(s)');
                            }
                        }
    
                        var timer2 = setInterval(function () {
                            set_time()
                        }, 1000);
                    }
                })
                .fail(() => {
                    message.showError('服务器请求超时,请重试!')
                });
    
    
        })
    

    效果器图


    04效果图2.jpg 05效果图3.jpg

    完整js

    $(() => {
        //1.点击刷新验证码
        $('.captcha-graph-img img').click(function () {
            $(this).attr('src', '/image_code/?rand=' + Math.random())
        });
    
        //定义状态变量
        //用户名是否准备
        let isUsernameReady = false,
            //密码是否准备
            isPasswordReady = false,
            //手机是否准备
            isMobileReady = false,
            //短信验证码是否准备
            isSmsCodeReady = false;
    
        //2. 校验功能
        //用户名校验,鼠标离开用户名输入框就校验
        let $username = $('#username');
        //blur()光标移开事件
        $username.blur(fnCheckUsername);
    
        function fnCheckUsername() {
            //校验用户名
            isUsernameReady = false;
            //获取输入的用户名
            let sUsername = $username.val();
            //用户名为空
            if (sUsername === '') {
                message.showError('用户名不能为空');
                //切记要返回,不然会一直运行
                return
            }
            //用户名格式不正确
            if (!(/^\w{5,20}$/).test(sUsername)) {
                message.showError('请输入5-20位的用户名');
                return
            }
            //发送ajax
            $.ajax({
                url: '/username/' + sUsername + '/',
                type: 'GET',
                dataType: 'json',
                success: function (res) {
                    if (res.data.count !== 0) {
                        message.showError(res.data.username + '用户名已被注册,请重新输入');
                    } else {
                        message.showInfo(res.data.username + '可以使用');
                        isUsernameReady = true
                    }
                },
                error: function () {
                    message.showError('服务器连接超时,请重试');
                }
            })
        }
    
        // 3.检测密码是否一致
        let $passwordRepeat = $('input[name="password_repeat"]');
        $passwordRepeat.blur(fnCheckPassword);
    
        function fnCheckPassword() {
            isPasswordReady = false;
            let password = $('input[name="password"]').val();
            let passwordRepeat = $passwordRepeat.val();
            if (password === '' || passwordRepeat === '') {
                message.showError('密码不能为空');
                return
            }
            if (password !== passwordRepeat) {
                message.showError('两次密码输入不一致');
                return
            }
            if (password === passwordRepeat) {
                isPasswordReady = true
            }
        }
    
        //4.校验手机号码
        let $mobile = $('#mobile');
        //blur()光标移开事件
        $mobile.blur(fnCheckMobile);
    
        function fnCheckMobile() {
            isMobileReady = false;
            let sMobile = $mobile.val();
            if (sMobile === '') {
                message.showError('手机号码不能为空,请重新输入!');
                return
            }
            if (!(/^1[3,9]\d{9}$/).test(sMobile)) {
                message.showError('请重新输入11位的手机号码!');
                return
            }
            //发送ajax
            $.ajax({
                url: '/mobile/' + sMobile + '/',
                type: 'GET',
                dataType: 'json',
                success: function (res) {
                    if (res.data.count !== 0) {
                        message.showError('手机号码' + res.data.mobile + '已被注册,请重新输入');
                    } else {
                        message.showInfo('手机号码' + res.data.mobile + '可以使用');
                        isMobileReady = true
                    }
                },
                error: function () {
                    message.showError('服务器连接超时,请重试');
                }
            })
        }
    
        //5.发送短信验证码
        let $smsBurtton = $('.sms-captcha');
        $smsBurtton.click(() => {
            //首先拿到图形验证码
            let sCaptcha = $('input[name="captcha_graph"]').val();
            if (sCaptcha === '') {
                message.showError('孩子 请输入图形验证码!!');
                return
            }
            //判断手机号码是否准备好
            if (!isMobileReady) {
                fnCheckMobile();
                return
            }
            //若都校验好了则发送ajax
            $
                .ajax({
                    url: '/sms_code/',
                    type: 'POST',
                    data: {
                        mobile: $mobile.val(),
                        captcha: sCaptcha,
                    },
                    dataType: 'json',
                })
                // 相当于success:function(res){}
    
                .done((res) => {
                    if (res.errno !== '0') {
                        message.showError(res.errmsg)
                    } else {
                        message.showSuccess(res.errmsg);
                        $smsBurtton.attr('disabled',true);
                        //倒计时
                        var a = 60;
                        function set_time() {
                            if (a == 1) {
                                clearInterval(timer2);
                                //document.getElementById("sms-captcha").innerHTML = "获取短信验证码";
                                $smsBurtton.html("获取短信验证码");
                                $smsBurtton.removeAttr('disabled');
                            } else {
                                a--;
                                $smsBurtton.html (a+'(s)');
                            }
                        }
    
                        var timer2 = setInterval(function () {
                            set_time()
                        }, 1000);
                    }
                })
                .fail(() => {
                    message.showError('服务器请求超时,请重试!')
                });
    
    
        })
    
    });
    

    最后的最后

     $ git add .
     $ git commit -m 'update projects'
     $ git push origin master
    

    相关文章

      网友评论

          本文标题:Django项目用户注册功能(三)

          本文链接:https://www.haomeiwen.com/subject/lcsljctx.html