【Python百日进阶-WEB开发】Day179 - Django案例:11短信验证码
创始人
2024-01-25 23:51:10
0

文章目录

  • 九、短信验证码
    • 9.1 短信验证码逻辑分析
    • 9.2 容联云通讯短信平台
      • 9.2.1 容联云通讯短信平台介绍
      • 9.2.2 容联云通讯短信SDK测试
        • 9.2.2.1 美多商城meiduo_mall.apps.verifications.libs中新建yuntongxun包,结构如下:
        • 9.2.2.2 ccp_sms.py代码
        • 9.2.2.3 CCPRestSDK.py ,python3代码
        • 9.2.2.4 测试发送短信
      • 9.2.3 封装发送短信单例类
    • 9.3 短信验证码接口设计和定义
    • 9.4 短信验证码后端逻辑
    • 9.5 短信验证码前端逻辑

九、短信验证码

9.1 短信验证码逻辑分析

在这里插入图片描述

9.2 容联云通讯短信平台

了解容联云通讯平台和短信SDK的使用方式,

9.2.1 容联云通讯短信平台介绍

  1. 容联云官网:https://www.yuntongxun.com/
    在这里插入图片描述
    2.注册登录
    在这里插入图片描述
  2. 通过认证,企业认证或个人认证,提交申请后一般需要第二天通过。发送短信0.06元/条,注册赠送8元,我发过一条了。
    在这里插入图片描述
  3. 添加容联云子应用,通过认证后可以上线应用
    在这里插入图片描述
  4. 添加测试号码
    在这里插入图片描述
  5. 短信模板
    在这里插入图片描述
  6. Python Demo中模板短信的使用说明
    https://doc.yuntongxun.com/p/5a533e0c3b8496dd00dce08c
    在这里插入图片描述
  7. 开发文档-SDK接口文件
    https://www.yuntongxun.com/doc/ready/demo/1_4_1_2.html
    在这里插入图片描述

9.2.2 容联云通讯短信SDK测试

9.2.2.1 美多商城meiduo_mall.apps.verifications.libs中新建yuntongxun包,结构如下:

在这里插入图片描述

9.2.2.2 ccp_sms.py代码

#-*- coding: UTF-8 -*-  from meiduo_mall.apps.verifications.libs.yuntongxun.CCPRestSDK import REST
# import ConfigParser
import ssl# 全局取消证书验证
ssl._create_default_https_context = ssl._create_unverified_context  #主帐号
accountSid= '8aaf07 这里填真实的主账号 5b0963df1';#主帐号Token
accountToken= 'b809 这里填真实的Token 4018733';#应用Id
appId='8a21 这里填真实的AppID 10d53ba6';#请求地址,格式如下,不需要写http://
serverIP='app.cloopen.com';#请求端口 
serverPort='8883';#REST版本号
softVersion='2013-12-26';# 发送模板短信# @param to 手机号码# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''# @param $tempId 模板Iddef sendTemplateSMS(to,datas,tempId):#初始化REST SDKrest = REST(serverIP,serverPort,softVersion)rest.setAccount(accountSid,accountToken)rest.setAppId(appId)result = rest.sendTemplateSMS(to,datas,tempId)print(result)#sendTemplateSMS(手机号码,内容数据,模板Id)
if __name__ == '__main__':# 注意测试的短信模板编号为1,短信验证码为123456,有效期为5分钟sendTemplateSMS('13953800865', ['123456', 5], 1)

9.2.2.3 CCPRestSDK.py ,python3代码

这个文件官网示例文件使用python2.7写的,有五六个地方需要修改,这是修改测试过的python3代码
修改内容主要包括:
1、头部导包
2、MD5加密
3、req.add_data
4、base64加密等

from hashlib import md5
import base64
import datetime
import urllib.request	# py3
import json
from meiduo_mall.apps.verifications.libs.yuntongxun.xmltojson import xmltojson	# py3
from xml.dom import minidom class REST:AccountSid=''AccountToken=''AppId=''SubAccountSid=''SubAccountToken=''ServerIP=''ServerPort=''SoftVersion=''Iflog=True #是否打印日志Batch=''  #时间戳BodyType = 'xml'#包体格式,可填值:json 、xml# 初始化# @param serverIP       必选参数    服务器地址# @param serverPort     必选参数    服务器端口# @param softVersion    必选参数    REST版本号def __init__(self,ServerIP,ServerPort,SoftVersion):self.ServerIP = ServerIP;self.ServerPort = ServerPort;self.SoftVersion = SoftVersion;# 设置主帐号# @param AccountSid  必选参数    主帐号# @param AccountToken  必选参数    主帐号Tokendef setAccount(self,AccountSid,AccountToken):self.AccountSid = AccountSid;self.AccountToken = AccountToken;   # 设置子帐号# # @param SubAccountSid  必选参数    子帐号# @param SubAccountToken  必选参数    子帐号Tokendef setSubAccount(self,SubAccountSid,SubAccountToken):self.SubAccountSid = SubAccountSid;self.SubAccountToken = SubAccountToken;    # 设置应用ID# # @param AppId  必选参数    应用IDdef setAppId(self,AppId):self.AppId = AppId; def log(self,url,body,data):print('这是请求的URL:')print (url);print('这是请求包体:')print (body);print('这是响应包体:')print (data);print('********************************')# 创建子账号# @param friendlyName   必选参数      子帐号名称def CreateSubAccount(self, friendlyName):self.accAuth()nowdate = datetime.datetime.now()self.Batch = nowdate.strftime("%Y%m%d%H%M%S")#生成sigsignature = self.AccountSid + self.AccountToken + self.Batch;signature = signature.encode('utf-8') # py3# sig = md5.new(signature).hexdigest().upper()sig = md5(signature).hexdigest().upper() # py3#拼接URLurl = "https://"+self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SubAccounts?sig=" + sig#生成authsrc = self.AccountSid + ":" + self.Batch;# auth = base64.encodestring(src).strip()auth = base64.encodestring(src.encode()).strip() 	# py3req = urllib.request.Request(url)self.setHttpHeader(req)req.add_header("Authorization", auth)#xml格式body ='''%s\%s\\'''%(self.AppId, friendlyName)if self.BodyType == 'json': #json格式body = '''{"friendlyName": "%s", "appId": "%s"}'''%(friendlyName,self.AppId)data=''# req.add_data(body)req.data = body.encode() # py3try:res = urllib.request.urlopen(req);data = res.read()res.close()if self.BodyType=='json':#json格式locations = json.loads(data)else:#xml格式xtj=xmltojson()locations=xtj.main(data)if self.Iflog:self.log(url,body,data)return locationsexcept Exception as error:if self.Iflog:self.log(url,body,data)return {'172001':'网络错误'}

9.2.2.4 测试发送短信

1.vscode终端输出
在这里插入图片描述

  1. 测试手机收到的短信
    在这里插入图片描述

9.2.3 封装发送短信单例类

问题:如果同时发送多个短信验证码,那么就会同时创建多个RET SDK的对象,会消耗很多额外的内存空间。
解决方法:使用单例类,它的特点是只有一个实例存在
使用场景:当我们希望在整个系统中,某个类只出现一个实例时,就可以使用单例类设计模式
改写后的代码:

#-*- coding: UTF-8 -*-  from meiduo_mall.apps.verifications.libs.yuntongxun.CCPRestSDK import REST
# import ConfigParser
import ssl# 全局取消证书验证
ssl._create_default_https_context = ssl._create_unverified_context  #主帐号
accountSid= '8aaf07087a331dc7017afb85b0963df1';
#主帐号Token
accountToken= 'b809c84015db41c8a4a3d84224018733';
#应用Id
appId='8a216da87a332d53017afb8d10d53ba6';
#请求地址,格式如下,不需要写http://
serverIP='app.cloopen.com';
#请求端口 
serverPort='8883';
#REST版本号
softVersion='2013-12-26';
# 发送模板短信
# @param to 手机号码
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
# @param $tempId 模板Id# def sendTemplateSMS(to,datas,tempId):
#     #初始化REST SDK
#     rest = REST(serverIP,serverPort,softVersion)
#     rest.setAccount(accountSid,accountToken)
#     rest.setAppId(appId)#     result = rest.sendTemplateSMS(to,datas,tempId)
#     print(result)class CCP(object):""" 发送短信验证码的单例类 """def __new__(cls, *args, **kwargs):""" 定义单例化的初始化方法,返回值为单例 """# 判断单例是否存在,利用动态赋值的_instance属性。如果单例不存在,就初始化单例if not hasattr(cls, '_instance'):cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs)#初始化REST SDK,赋值给单例属性,实现与单例同生共死,唯一存在cls._instance.rest = REST(serverIP,serverPort,softVersion)cls._instance.rest.setAccount(accountSid,accountToken)cls._instance.rest.setAppId(appId)# 返回单例return cls._instance  def send_template_sms(self, to, datas, tempId):""" 定义对象方法,发送短信验证码 to:手机号码,字符串,多个手机号码用逗号分隔datas:发送内容,双元素列表,第一个元素为验证码字符串,第二个元素为整数有效时间(分钟)tempID:模板ID,测试模板为1返回值:成功:0,失败:-1"""result = self.rest.sendTemplateSMS(to,datas,tempId)print(result)# 根据发送是否成功返回0或-1if result.get('statusCode') == '000000':return 0else:return -1#sendTemplateSMS(手机号码,内容数据,模板Id)
if __name__ == '__main__':# 注意测试的短信模板编号为1,短信验证码为123456,有效期为5分钟# sendTemplateSMS('13953800865', ['123456', 5], 1)# 单例类发送短信验证码CCP().send_template_sms('13953800865', ['6543258', 5], 1)

9.3 短信验证码接口设计和定义

在这里插入图片描述

9.4 短信验证码后端逻辑

  1. verifications.urls.py中
from django.urls import path, re_path
from . import viewsapp_name = 'verifications'urlpatterns = [# 图形验证码,re_path路由正则校验,响应json数据,不需要重定向,也就不需要命名空间re_path(r'^image_codes/(?P[\w-]+)/$', views.ImageCodeView.as_view()),# 短信验证码,re_path路由正则校验,响应json数据,不需要重定向,也就不需要命名空间re_path(r'^sms_codes/(?P1[3-9]\d{9})/$', views.SMSCodeView.as_view()),]
  1. verifications.views.py中的短信验证码类视图
from django.views import View
from django_redis import get_redis_connection
from django import http
import random, loggingfrom meiduo_mall.apps.verifications.libs.captcha.captcha import captcha
from . import constants
from meiduo_mall.utils.response_code import RETCODE
from meiduo_mall.apps.verifications.libs.yuntongxun.ccp_sms import CCP# 创建日志输出器
logger = logging.getLogger('django')class SMSCodeView(View):""" 短信验证码 """def get (self, request, mobile):"""param:request,请求对象;mobile,手机号return:JSON"""""" 接收和校验参数 """# 接收参数image_code_client = request.GET.get('image_code')uuid = request.GET.get('uuid')# 校验参数,mobile不需要视图内校验,在路由处已经校验完毕了,错误进不了视图if not all([image_code_client, uuid]):return http.HttpResponseForbidden('缺少必传参数!')# 判断用户是否频繁发送短信验证码redis_conn = get_redis_connection('verify_code')     # 创建redis库的连接send_flag = redis_conn.get(f'send_flag_{mobile}')if send_flag:   #  已存在return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信验证码过于频繁!'})"""" 主体业务逻辑 """# 1.从redis库中提取图形验证码image_code_server = redis_conn.get(f'img_{uuid}')if image_code_server is None:return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码已失效!'})# 2.删除redis中存储的图形验证码redis_conn.delete(f'img_{uuid}')# 3.对比图形验证码image_code_server = image_code_server.decode()   # 提取的数据时bytes类型,需要转换为字符串if image_code_client.lower() != image_code_server.lower():  # 全部转为小写return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误!'})# 4.生成短信验证码:随机6位数字,不足前面补0, 000007sms_code = '%06d' % random.randint(0, 999999)logger.info(sms_code)   # 手动输出短信验证码的日志# 5.保存短信验证码,为优化redis的性能,使用管道队列操作# 5.1 创建redis pipeline管道队列pl = redis_conn.pipeline()# 5.2 将命令添加到队列中pl.setex(f'sms_{mobile}', constants.SMS_CODE_REDIS_EXPIRES, sms_code)   # sms_code存储到redis数据库pl.setex(f'send_flag_{mobile}', constants.SEND_SMS_CODE_INTERVAL, 1)    # 保存短信验证码标记,有效期60秒,标记1表示60秒内给该手机发送了验证码# 5.3 执行队列命令pl.excute()# 6.单例类发送短信验证码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': '短信验证码发送成功!'})

9.5 短信验证码前端逻辑

  1. register.html中的短信验证码部分
  • [[ sms_code_tip ]][[ error_sms_code_message]]
    1. register.js中的方法
    //发送手机验证码
    send_sms_code(){//避免恶意用户频繁点击获取短信验证码的A标签if (this.send_flag == true) {   //已经点击了发送短信验证码return;}this.send_flag = true;      //校验用户输入的mobile和image_codethis.check_mobile();this.check_image_code();if (this.error_image_code == true || this.error_mobile == true) {this.send_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') {    //发送短信验证码成功//展示倒计时60S效果 setInterval('回调函数', '时间间隔1000毫秒')let num = 60;let t = setInterval(() => {     // t 为定时器编号if (num == 1){          //倒计时即将结束clearInterval(t);   // 停止回调函数的执行this.sms_code_tip = '获取短信验证码';   // 还原 sms_code_tip 的提示信息this.generate_image_code();     //重新生成图形验证码this.send_flag == false;} else {                // 正在倒计时num -= 1;this.sms_code_tip = num + '秒';}}, 1000)} else {    if (response.data.code == '4001') { // 图形验证码错误// 渲染错误信息this.error_image_code_message = response.data.errmsg;this.error_image_code = true;this.send_flag == false;}}}).catch(error => {console.log(error.response);this.send_flag == false;})
    },
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    相关内容

    热门资讯

    it男成功创业故事 it男成功... 他叫潘卫国,2011年毕业于江苏科技大学计算机科学与技术专业。短短3年,他从给别人打工到开公司创业,...
    80后年轻大学生的it创业故事... 80后大学生张宇毕业后,来到大连,经过多次求职,均不顺利,后来决定报名参加IT培训。为此,张宇多方打...
    干事创业敢担当 干事创业敢担当... 本报评论员能否敢于负责、勇于担当,最能看出一个干部的党性和作风。“干事创业敢担当”,是第二批主题教育...
    100个创业小点子,我们应该怎... 100个创业小点子,我们应该怎么样更好的创业来源:•2020-05-创业是创业者对自己拥有的资源或通...
    淘宝创业故事 淘宝创业故事 淘... 现如今淘宝创业十分流行,淘宝创业怎么样呢,小编整理了几篇有关淘宝创业的文章,一起来看看吧淘宝创业故事...
    一位90后淘宝店主讲述的创业故... 淘宝店主一般被称为集贸市场的小商贩,很少有知识和技术上的投入。随着互联网的普及,越来越多有技术和有前...
    个人创业经历(人生必须要有的创... 我创业的初衷很简单,就是想赚大钱,能做个人上人!我打小就对赚钱感兴趣,从大学开始就不安分,当同龄人开...
    从一个街头小混混到身价千万大老... 今天要分享的这个创业故事里的主人公,在我看来,简直就是个牛人!他初中没毕业丶普通话很蹩脚,长得还行但...
    我把个人的创业经历写一下给大家... 只是后来他有女友了,他女友不能看我闲着,一看我闲着,就难受。我啥也没说,就走了。我自己开一家公司就是...
    大学生创业优惠政策相关内容 大... 又是一年毕业季,如今很多大学生都面临毕业就业的问题,也有小部分同学会选择创业,那我国的创业政策怎么样...