django2.1.7从0开始搭建一个个人博客网站第7天

使用第三方平台发送短信验证码

云通讯

七、发送短信功能

1.分析

请求方法**:POST

url定义/sms_code/

请求参数:url路径参数

参数类型是否必传描述
mobile字符串用户输入
image___code__-iduuidjs生成的图片uuid号
text字符串用户输入的图片验证码文本
2.后端代码实现
分析

1,获取参数
2,验证参数
3,发送短信
4,保存短信验证码
5,返回给前端
send mobile sms code
POST /sms_code/

  • 检查图片验证码是否正确
  • 检查是否在60s内有发送记录
  • 生成短信验证码
  • 保存短信验证码与发送记录
  • 发送短信

将云通讯接口放到utils文件夹下:
在这里插入图片描述
把这些换成自己的云通讯账号:
在这里插入图片描述
测试的时候要添加测试号码,只有添加的号码才能发送短信在这里插入图片描述
测试是否能发送短信:
在sms.py文件下修改发送手机号为自己的测试手机号
运行sms.py文件

在这里插入图片描述

1、后台视图 views.py

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

    def post(self, request):
        """
        :param reqeust: 请求对象
        :param mobile: 手机号
        :return: JSON
        """
        # 接收参数
        json_str = request.body
        dict_data = json.loads(json_str)

        image_code_client = dict_data.get('text')
        uuid = dict_data.get('image_code_id')
        mobile = dict_data.get('mobile')
        # 校验参数
        if not all([image_code_client, uuid,mobile]):
            return res_json(errno=Code.PARAMERR,errmsg='参数错误')
        # 创建连接到redis的对象
        redis_conn = get_redis_connection('verify_codes')
        # 提取数据库的图形验证码
        image_code_server = redis_conn.get('img_%s' % uuid)
        if image_code_server is None:
            # 图形验证码过期或者不存在
            return res_json(errno=Code.PARAMERR,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 res_json(errno=Code.PARAMERR, errmsg= '输入图形验证码有误')

        # 生成短信验证码:生成6位数验证码
        sms_code = '%06d' % random.randint(0, 999999)
        logger.info(sms_code)

        # 限定频繁发送验证码
        send_flag = redis_conn.get('send_flag_%s' % mobile)
        if send_flag:
            return res_json(errno=Code.DATAEXIST, errmsg='发送短信过于频繁')
   		
        redis_conn.setex('sms_%s' % mobile, 300, sms_code)
        # 重新写入send_flag
        redis_conn.setex('send_flag_%s' % mobile, 500, 1)
        # 执行请求
        # 发送短信
        logger.info('短信验证码: {}'.format(sms_code))
        logging.info('发送短信正常[mobile:%s sms_num:%s]' % (mobile,sms_code) )

        # 发送短信验证码
        # ccp = CCP()
        # ccp.send_template_sms(mobile, [sms_code, 5], 1)
        # 响应结果
        return res_json(errmsg='短信验证码发送成功')

2、url路径 urls.py


from django.urls import path,re_path

from . import views
app_name = 'verifications'
urlpatterns = [
    path('image_code/<uuid:image_code>/',views.ImageView.as_view(),name='image_code'),
    re_path('username/(?P<username>\w{5,20})/',views.UsernameView.as_view(),name='username'),
    re_path('mobiles/(?P<mobile>1[3-9]\d{9})/', views.MobileView.as_view(), name='mobile'),
    path('sms_code/', views.SMSCodeView.as_view(), name='sms_code'),

]

3、前端代码 js

$(function () {
    let $username = $('#user_name'); //获取用户名
    let $img = $(".form-item .captcha-graph-img img");   // 获取图像
    let sImageCodeId = ""; //uuid
    let $mobile = $('#mobile');  // 选择id为mobile的网页元素,需要定义一个id为mobile手机号

     // 校验功能
    // 定义一些状态变量
    let isUsernameReady = false,
        isPasswordReady = false,
        isMobileReady = false;
        send_flag = true;    // 短信标记

    // let $imgCodeText = $('#input_captcha');
    // console.log(img);
    genreate();
    $img.click(genreate);
    function genreate() {
		sImageCodeId = generateUUID();
        let imageCodeUrl = '/image_code/' + sImageCodeId + '/';

        $img.attr('src', imageCodeUrl)
    }

     // 生成图片UUID验证码
    function generateUUID() {
    let d = new Date().getTime();
    if (window.performance && typeof window.performance.now === "function") {
        d += performance.now(); //use high-precision timer if available
    }
    let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        let r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    return uuid;
    }


    // 2、用户名验证逻辑
    // blur,触发失去焦点事件
    $username.blur(function () {
    fn_check_username();
    });

    // 判断用户名是否已经注册
    function fn_check_username() {
        // 校验用户名
        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',
    })
      .done(function (res) {
        if (res.data.count === 1) {
          message.showError(res.data.username + '已注册,请重新输入!');
        } else {
          message.showSuccess(res.data.username + '能正常使用!');
          isUsernameReady = true
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
      });
    }

    //3、手机号验证
    // 手机号校验,光标离开手机号输入框就校验
    $mobile.blur(fn_check_mobile);

    // 判断手机号是否注册
    function fn_check_mobile() {
        //检验手机号
        isMobileReady = false;
    let sMobile = $mobile.val();  // 获取用户输入的手机号码字符串
    if (sMobile === "") {
      message.showError('手机号不能为空!');
      return
    }
    if (!(/^1[345789]\d{9}$/).test(sMobile)) {
      message.showError('手机号码格式不正确,请重新输入!');
      return
    }

    $.ajax({
      url: '/mobiles/' + sMobile + '/',
      type: 'GET',
      dataType: 'json',
      async: false
    })
      .done(function (res) {
        if (res.data.count === 1) {
          message.showError(res.data.mobile + '已注册,请重新输入!');
        } else {
          message.showSuccess(res.data.mobile + '能正常使用!');
          isMobileReady = true;
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
      });
    }

    // 4、发送短信验证码逻辑
    let $smsCodeBtn = $('.form-item .sms-captcha');  // 获取短信验证码按钮元素,需要定义一个id为input_smscode
    let $imgCodeText = $('#input_captcha');  // 获取用户输入的图片验证码元素,需要定义一个id为input_captcha

    $smsCodeBtn.click(function () {
    // 判断手机号是否输入
      if(send_flag){

          send_flag = false;
          // 判断手机号码是否准备好
        if(!isMobileReady){
            fn_check_mobile();
            return
        }
    // 13535353535
    // 判断用户是否输入图片验证码
    let text = $imgCodeText.val();  // 获取用户输入的图片验证码文本
    if (!text) {
        message.showError('请填写验证码!');
        return
    }

    if (!sImageCodeId) {
      message.showError('图片UUID为空');
      return
    }

    // 正常
    let SdataParams = {
      "mobile": $mobile.val(),   // 获取用户输入的手机号
      "text": text,   // 获取用户输入的图片验证码文本
      "image_code_id": sImageCodeId  // 获取图片UUID
    };
    // 向后端发送请求
    $.ajax({
      // 请求地址
      url: "/sms_code/",
      // 请求方式
      type: "POST",
      // 向后端发送csrf token

      data: JSON.stringify(SdataParams),
      // data: JSON.stringify(SdataParams),
      // 请求内容的数据类型(前端发给后端的格式)
      contentType: "application/json; charset=utf-8",
      // 响应数据的格式(后端返回给前端的格式)
      dataType: "json",
    })

      .done(function (res) {
          console.log(res);

        if (res.errno === "0") {
          // 倒计时60秒,60秒后允许用户再次点击发送短信验证码的按钮
           message.showSuccess('短信验证码发送成功');
          let num = 60;
          // 设置一个计时器
          let t = setInterval(function () {
            if (num === 1) {
              // 如果计时器到最后, 清除计时器对象
              clearInterval(t);
              // 将点击获取验证码的按钮展示的文本恢复成原始文本
              $smsCodeBtn.html("获取验证码");
              send_flag = true;
            } else {
              num -= 1;
              // 展示倒计时信息
              $smsCodeBtn.html(num + "秒");
            }
          }, 1000);
        }
        else {
          message.showError(res.errmsg);
          send_flag = true;
        }
      })

      .fail(function(){
        message.showError('服务器超时,请重试!');
      });
      }

    });




});


关于csrf_token的问题:

我前面是把csrf_token的中间件给注释了
我们可以看到在中间件中有这样一个函数

class CsrfViewMiddleware(MiddlewareMixin):

	    def _get_token(self, request):
        if settings.CSRF_USE_SESSIONS:
            try:
                return request.session.get(CSRF_SESSION_KEY)
            except AttributeError:
                raise ImproperlyConfigured(
                    'CSRF_USE_SESSIONS is enabled, but request.session is not '
                    'set. SessionMiddleware must appear before CsrfViewMiddleware '
                    'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
                )
        else:
            try:
                cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
            except KeyError:
                return None

            csrf_token = _sanitize_token(cookie_token)
            if csrf_token != cookie_token:
                # Cookie token needed to be replaced;
                # the cookie needs to be reset.
                request.csrf_cookie_needs_reset = True
            return csrf_token


		#在views.py之前进行
	    def process_request(self, request):
        csrf_token = self._get_token(request)
        if csrf_token is not None:
            # Use same token next time.
            request.META['CSRF_COOKIE'] = csrf_token


我们看到在process_request中会拿到get_token得到前端传来的cookie中token的值,与post请求csrf_token进行比较,因此post等修改操作的请求我们必须前端传来token,不然就会报错,生成token的方式有很多 比如配合form的{%csrf_token%} 也可以设置全局的token值
这里使用中间件生成token值,作用与所有视图和模板

生成token的方式很多,本项目使用中间件生成token:
在utils文件下创建一个middlewares.py文件

from django.middleware.csrf import get_token
from django.utils.deprecation import MiddlewareMixin

class Middleware(MiddlewareMixin):
	
	def process_request(self,request):
		get_token(request)

别忘了要到设置文件中添加中间件

js:

还要修改ajax:(部分)请求头中加入
$.ajax({
      // 请求地址
      url: "/sms_code/",
      // 请求方式
      type: "POST",
      // 向后端发送csrf token
      headers: {
                // 根据后端开启的CSRFProtect保护,cookie字段名固定为X-CSRFToken
                "X-CSRFToken": getCookie("csrftoken")
      },

      data: JSON.stringify(SdataParams),
      // data: JSON.stringify(SdataParams),
      // 请求内容的数据类型(前端发给后端的格式)
      contentType: "application/json; charset=utf-8",
      // 响应数据的格式(后端返回给前端的格式)
      dataType: "json",
    })




#得到cookie的值
// get cookie using jQuery
  function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      let cookies = document.cookie.split(';');
      for (let i = 0; i < cookies.length; i++) {
        let cookie = jQuery.trim(cookies[i]);
        // 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;
  }

在这里插入图片描述
可以看到为每个页面都设置了cookie值

总结:

1、使用第三方发送短信
2、图形验证码的校验、redis手机验证码保存300秒,在60秒内不能重复发送短信,60秒后发送就会覆盖原来的键
3、csrf_token的问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值