vue_drf實現簡訊驗證碼
阿新 • • 發佈:2021-07-18
目錄
- 一、需求
- 1,需求
- 二、sdk引數配置
- 1,目錄結構
- 三、程式碼實現
- 1,後端程式碼
- 2,前端程式碼
一、需求
1,需求
我們在做開發時,登入頁面很多情況下是可以用手機號接收簡訊驗證碼,然後實現登入的,那我們今天就來做一做這一功能。
虛擬碼:
進入登入頁面,點選簡訊登入
輸入手機號碼,點選獲取驗證碼,後端在redis裡儲存驗證碼
使用者把手機收到的驗證碼輸入,點選登入,會把手機號和驗證碼發往後端,然後進行驗證
要想傳送簡訊,讓使用者收到簡訊,我們的藉助一個容聯雲的介面,註冊一個賬號。
使用時需要的一些引數:
下載sdk
1.。。。。。。。
2.。。。。。。
3.。。。。。。。
下載完成後,解壓。放入我們drf專案的apps裡的libs裡
二、sdk引數配置
1,目錄結構
2,配置sms.py檔案
# -*- coding:utf-8 -*- from .CCPRestSDK import REST # 說明:主賬號,登陸雲通訊網站後,可在"控制檯-應用"中看到開發者主賬號ACCOUNT SID _accountSid = 'xxxxxxxxxxxxx' # 8a216da863f8e6c20164139687e80c1b # 說明:主賬號Token,登陸雲通訊網站後,可在控制檯-應用中看到開發者主賬號AUTH TOKEN _accountToken = 'xxxxxxxxxxxxx' # 6dd01b2b60104b3dbc88b2b74158bac6 # 請使用管理控制檯首頁的APPID或自己建立應用的APPID _appId = '8aaf0708697b6beb01699f3c645f1766' # 8a216da863f8e6c20164139688400c21 # 說明:請求地址,生產環境配置成app.cloopen.com _serverIP = 'sandboxapp.cloopen.com' # 說明:請求埠 ,生產環境為8883 _serverPort = "8883" # 說明:REST API版本號保持不變 _softVersion = '2013-12-26' #下面的內容不用修改 class CCP(object): """傳送簡訊的輔助類""" def __new__(cls,*args,**kwargs): # 判斷是否存在類屬性_instance,_instance是類CCP的唯一物件,即單例 if not hasattr(CCP,"_instance"): cls._instance = super(CCP,cls).__new__(cls,**kwargs) 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,temp_id): """傳送模板簡訊""" # @param to 手機號碼 # @param datas 內容資料 格式為陣列 例如:{'12','34'},如不需替換請填 '' # @param temp_id 模板Id result = self.rest.sendTemplateSMS(to,temp_id) # 如果雲通訊傳送簡訊成功,返回的字典資料result中statuCode欄位的值為"000000" if result.get("statusCode") == "000000": # 返回0 表示傳送簡訊成功 return 0 else: # 返回-1 表示傳送失敗 return -1 if __name__ == '__main__': ccp = CCP() # 注意: 測試的簡訊模板編號為1 ccp.send_template_sms('15914397060',['1234',5],1)
三、程式碼實現
1,後端程式碼
views.py,這是獲取驗證碼請求的處理,也就是後端產生一個隨機碼,傳送給手機使用者,然後把隨機碼儲存於redis中,然後給前端返回一個驗證碼傳送成功的訊號
from .models import User from rest_framework import status from lufei_drf.libs.yuntongxun.sms import CCP from django_redis import get_redis_connection class SMSCodeAPIView(APIView): def get(self,request): # 1. 通過查詢字串獲取手機號碼 phone = request.query_params.get("phone") ty=request.query_params.get('type') # 2. 傳送簡訊之前驗證碼驗證一下手機號碼 if ty=='register': try: User.objects.get(phone=phone) return Response({"message": "當前手機號已經被註冊過"},status=status.HTTP_400_BAD_REQUEST) except: pass redis = get_redis_connection("sms_code") if redis.get("times_%s" % phone): return Response({"message": "當前手機號已經在一分鐘內傳送過簡訊"},status=status.HTTP_400_BAD_REQUEST) # 3. 使用手機號碼傳送簡訊驗證碼 # 生成一個簡訊驗證碼 sms_code = "%04d" % random.randint(0,9999) ccp = CCP() result = ccp.send_template_sms(phone,[sms_code,"5分鐘"],1) if result == 0: # 傳送簡訊成功,儲存簡訊驗證碼到redis中 # 開啟管道操作 pl = redis.pipeline() pl.multi() # 接下來會在管道中執行多條命令 # setex(變數名,有效期[秒],值 ) SMS_EXPIRE_TIME = 5 * 60 # 簡訊驗證碼的有效期 SMS_TIMES = 60 # 簡訊傳送的間隔時間 # 把原來立即執行的命令放置到管道 pl.setex("sms_%s" % phone,SMS_EXPIRE_TIME,sms_code) pl.setex("times_%s" % phone,SMS_TIMES,1) # 統一執行管道中的命令 pl.execute() # 4. 響應資料給客戶端 return Response({"message":result},status=status.HTTP_200_OK)
urls.py
from django.urls import path # jwt內部實現的登陸檢視 from rest_frwITtADeamework_jwt.views import obtain_jwt_token from .views import SMSCodeAPIView,urlpatterns=[ path(r"login/",obtain_jwt_token ),path('sms/',SMSCodeAPIView.as_view()),]
utils.py,這是對使用者提交手機驗證碼後,對手機號和驗證碼的校對。判斷都正確後,返回一個物件,包括token,user資訊等,
from django.contrib.auth.backends import ModelBackend from django_redis import get_redis_connection def jwt_response_payload_handler(token,user=None,request=None): """ 自定義jwt認證成功返回資料 :token 返回的jwt :user 當前登入的使用者資訊[物件] :request 當前本次客戶端提交過來的資料 """ return { 'token': token,'id': user.id,'username': user.username,} #實現多功能登入 import re from .models import User#查詢使用者名稱或手機號是否已經是我們的使用者 def get_user_by_account(account): """ 根據帳號獲取user物件 :param account: 賬號,可以是使用者名稱,也可以是手機號 :return: User物件 或者 None """ try: if re.match('^1[3-9]\d{9}$',account): # 帳號為手機號 user = User.objects.get(phone=account) else: # 帳號為使用者名稱 user = User.objects.get(username=account) except User.DoesNotExist: return None else: return user #驗證使用者提交的簡訊和我們儲存在redis裡的資訊是否一致 def sms_code_verify(phone,sms_code): redis = get_redis_connection("sms_code") value=redis.get('sms_%s'%phone).decode() if value==sms_code: return TruwITtADee return False class UsernameMobileAuthBackend(ModelBackend): """ 自定義使用者名稱或手機號認證 """ def authenticate(self,request,username=None,password=None,**kwargs): user = get_user_by_account(username) #當密碼長度為4時,我判斷其為手機號和簡訊驗證碼登入 if len(password)==4 and user is not None and sms_code_verify(username,password): return user elif user is not None and user.check_password(password): return user else: return None
2,前端程式碼
login元件
<template> <div id="login"> <div class="box"> <p> <img src="../../assets/login_title.png" alt="_drf實現簡訊驗證碼"> </p> <p class="sign">幫助有志向的年輕人通過努力學習獲得體面的工作和生活!</p> <div class="pass" v-show="num==1"> <div class="title2 cursor"> <span @click="num=1" :class="num==1 ? 'show' :''">密碼登入</span> <span @click="num=2" :class="num==2 ? 'show' :''">簡訊登入</span> </div> <input v-model="username" type="text" class="ss" placeholder="使用者名稱 / 手機號碼"> <input v-model="password" type="password" class="ss" placeholder="密碼"> <div id="captcha" class="ss"></div> <div class="t1"> <div class="left"> <input type="checkbox" class="cursor" v-model="remenber"> <div class="remenber cursor" >記住密碼</div> </div> <div class="right cursor">忘記密碼</div> </div> <button class="login_btn" @click="login1">登入</button> <div class="register"> 沒有賬號 <span><router-link to="/register">立即註冊</router-link></span> </div> </div> <div class="messge" v-show="num==2"> <div class="title2 cursor"> <span @click="num=1" :class="num==1 ? 'show' :''">密碼登入</span> <span @click="num=2" :class="num==2 ? 'show' :''">簡訊登入</span> </div> <input v-model="phone" type="text" class="ss" placeholder="手機號碼"> <div class="sms"> <input v-model="sms_code" type="text" class="ss"> <div class="content" @click="get_sms_code">{{content}}</div> </div> <button class="login_btn" @click="sms_login">登入</button> <div class="register"> 沒有賬號 <span><router-link to="/register">立即註冊</router-link></span> </div> </div> </div> </div> </template> <script> export default { name:'login',data:function () { return { num:1,username:'',password:'',remenber:'',status:'',content:'獲取驗證碼',phone:'',sms_code:'',} },methods:{ //手機號和簡訊驗證碼登入 sms_login:function(){ let _this=this; this.$axios.post('http://127.0.0.1:8000/user/login/',{ 'username':_this.phone,'password':_this.sms_code,},{responseType:'on'}) .then(function (res) { sessionStorage.token=res.data.token; _this.$router.go(-1); }).catch(function (error) { console.log(error.response) }); },//獲取簡訊驗證碼 get_sms_code:function(){ let reg = /1[3-9]{2}\d{8}/; if( reg.test(this.phone) ){ if(this.content == "獲取驗證碼"){ this.content=60; let _this=this; let tt=setInterval(function () { if (_this.content>=1){ _this.content-- } else { _this.content='獲取驗證碼'; clearInterval(tt) } },1000); this.$axios.get('http://127.0.0.1:8000/user/sms?type=login&phone='+this.phone) .then(function (res) { if(res.data.message==0){ alert('驗證碼傳送成功') } }).catch(function (error) { console.log(error.response) }) } }else { alert('手機號碼有誤') } },//使用者名稱和密碼登入 login1:function () { if (this.status==1){ let _this=this; this.$axios.post('http://127.0.0.1:8000/user/login/',{ 'username':_this.username,'password':_this.password,{responseType:'json'}) .then(function (res) { if (res.status==200){ if (_this.remenber){ sessionStorage.removeItem('token'); localStorage.token=res.data.token; } else { localStorage.removeItem('token'); sessionStorage.token=res.data.token } _this.$router.go(-1); } else { alert('使用者名稱或密碼錯誤') } }) .catch(function (error) { alert(error.response.data.non_field_errors[0]); console.log(error.response.data.non_field_errors); }); } else { alert('驗證碼錯誤') } },handlerPopup:function (captchaObj) { let _this=this; captchaObj.onSuccess(function () { var validate = captchaObj.getValidate(); _this.$axios.post("http://127.0.0.1:8000/user/yzm/",{ geetest_challenge: validate.geetest_challenge,geetest_validate: validate.geetest_validate,geetest_seccode: validate.geetest_seccode,{ responseType:"json",}).then(function (res) { _this.status=res.data.status }).catch(function (error) { console.log(error) }) }); captchaObj.appendTo("#captcha"); } },created:function () { let _this=this; this.$axios.get("http://127.0.0.1:8000/user/yzm") .then(function (res) { let data=JSON.parse(res.data); initGeetest({ width:'350px',gt: data.gt,challenge: data.challenge,product: "popup",offline: !data.success },_this.handlerPopup); }).catch(function (error) { console.log(error) }) } } </script> <style scoped> #login{ background: url('../../assets/Login.jpg'); background-size: 100% 100%; height: 100%; position: fixed; width: 100%; } .box{ width: 500px; height: 600px; margin: 0 auto; margin-top: 200px; text-align: center; } .box img{ width: 190px; height: auto; } .box p{ margin: 0; } .sign{ font-size: 18px; color: #fff; letter-spacing: .29px; padding-top: 10px; padding-bottom: 50px; } .pass{ width: 400px; height: 460px; margin: 0 auto; background-color: white; border-radius: 4px; } .messge{ width: 400px; height: 390px; margin: 0 auto; background-color: white; border-radius: 4px; } .title2{ width: 350px; font-size: 20px; color: #9b9b9b; padding-top: 50px; border-bottom: 1px solid #e6e6e6; margin: 0 auto; margin-bottom: 20px; } .ss{ width: 350px; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; margin-bottom: 20px; } .pass .t1{ width: 350px; margin: 0 auto; height: 20px; line-height: 20px; font-size: 12px; text-align: center; position: relative; } .t1 .right{ position: absolute; right: 0; } .remenber{ display: inline-block; position: absolute; left: 20px; } .left input{ position: absolute; left:0; width: 14px; height: 14px; } .login_btn{ width: 350px; height: 45px; background: #ffc210; border-radius: 5px; font-size: 16px; color: #fff; letter-spacing: .26px; margin-top: 30px; outline: none; border:none; cursor: pointer; } .register{ margin-top: 20px; font-size: 14px; color: #9b9b9b; } .register span{ color: #ffc210; cursor: pointer; } .cursor{ cursor: pointer; } .show{ display: inline-block; padding-bottom: 5px; border-bottom: 2px solid orange; color: #4a4a4a; } a{ text-decoration: none; color: #ffc210; } #captcha{ margin: 0 auto; height: 44px; } .sms{ position: relative; width: 350px; height: 45px; margin: 0 auto; line-height: 45px; } .sms .content{ position: absolute; top:0; right: 10px; color: orange; border-left: 1px solid orange; padding-left: 10px; cursor: pointer; } </style>
前端獲取簡訊驗證碼:
//獲取簡訊驗證碼 get_sms_code:function(){ let reg = /1[3-9]{2}\d{8}/; //當手機號為為真實手機號,才可以觸發獲取驗證碼 if( reg.test(this.phone) ){ //當頁面上顯示為‘獲取驗證碼'時,才可以觸發獲取驗證碼請求;當進入倒計時,點選不能觸發獲取驗證碼請求 if(this.content == "獲取驗證碼"){ //成功傳送獲取驗證碼請求之後開始倒計時60秒 this.content=60; let _this=this; let tt=setInterval(function () { if (_this.content>=1){ _this.content-- } else { _this.content='獲取驗證碼'; clearInterval(tt) } },
前端用手機號和簡訊驗證碼登入:
//獲取簡訊驗證碼 get_sms_code:function(){ let reg = /1[3-9]{2}\d{8}/; //當手機號為為真實手機號,才可以觸發獲取驗證碼 if( reg.test(this.phone) ){ //當頁面上顯示為‘獲取驗證碼'時,才可以觸發獲取驗證碼請求;當進入倒計時,點選不能觸發獲取驗證碼請求 if(this.content == "獲取驗證碼"){ //成功傳送獲取驗證碼請求之後開始倒計時60秒 this.content=60; let _this=this; let tt=setInterval(function () { if (_this.content>=1){ _this.content-- } else { _this.content='獲取驗證碼'; clearInterval(tt) } },
到此這篇關於vue_drf實現簡訊驗證碼的文章就介紹到這了,更多相關vue_drf簡訊驗證碼內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!