接下來,咱們把註冊過程當中一些註冊信息(例如:短信驗證碼)和session緩存到redis數據庫中。css
安裝django-redis。html
pip install django-redis
在settings.py配置中添加一下代碼:前端
# 設置redis緩存 CACHES = { # 默認緩存 "default": { "BACKEND": "django_redis.cache.RedisCache", # 項目上線時,須要調整這裏的路徑 "LOCATION": "redis://127.0.0.1:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } }, # 提供給xadmin或者admin的session存儲 "session": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } }, # 提供存儲短信驗證碼 "sms_code":{ "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/2", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } # 設置xadmin用戶登陸時,登陸信息session保存到redis SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "session"
關於django-redis 的使用,說明文檔可見http://django-redis-chs.readthedocs.io/zh_CN/latest/vue
django-redis提供了get_redis_connection的方法,經過調用get_redis_connection方法傳遞redis的配置名稱可獲取到redis的鏈接對象,經過redis鏈接對象能夠執行redis命令python
https://redis-py.readthedocs.io/en/latest/ios
使用範例:redis
from django_redis import get_redis_connection // 連接redis數據庫 redis_conn = get_redis_connection("default")
在登陸後的平臺上面獲取一下信息:數據庫
ACCOUNT SID:8aaf0708697b6beb01699f4442911776 AUTH TOKEN : b4dea244f43a4e0f90e557f0a99c70fa AppID(默認):8aaf0708697b6beb01699f4442e3177c Rest URL(生產): app.cloopen.com:8883 [項目上線時使用真實短信發送服務器] Rest URL(開發): sandboxapp.cloopen.com:8883 [項目開發時使用沙箱短信發送服務器]
找到sdkdemo進行下載django
在開發過程當中,爲了節約發送短信的成本,能夠把本身的或者同事的手機加入到測試號碼中.axios
把雲通信的sdk保存到libs目錄下, 並修改裏面的基本配置信息。
# -*- coding:utf-8 -*- from .CCPRestSDK import REST from django.conf import settings # 說明:主帳號,登錄雲通信網站後,可在"控制檯-應用"中看到開發者主帳號ACCOUNT SID _accountSid = settings.SMS_ACCOUNTSID # 說明:主帳號Token,登錄雲通信網站後,可在控制檯-應用中看到開發者主帳號AUTH TOKEN _accountToken = settings.SMS_ACCOUNTTOKEN # 6dd01b2b60104b3dbc88b2b74158bac6 # 請使用管理控制檯首頁的APPID或本身建立應用的APPID _appId = settings.SMS_APPID # 8a216da863f8e6c20164139688400c21 # 說明:請求地址,生產環境配置成app.cloopen.com _serverIP = settings.SMS_SERVERIP # 說明:請求端口 ,生產環境爲8883 _serverPort = "8883" # 說明:REST API版本號保持不變 _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) # for k, v in result.iteritems(): # # if k == 'templateSMS': # for k, s in v.iteritems(): # print '%s:%s' % (k, s) # else: # print '%s:%s' % (k, v) class CCP(object): """發送短信的輔助類""" def __new__(cls, *args, **kwargs): # 判斷是否存在類屬性_instance,_instance是類CCP的惟一對象,即單例 if not hasattr(CCP, "_instance"): cls._instance = super(CCP, cls).__new__(cls, *args, **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, datas, temp_id) # 若是雲通信發送短信成功,返回的字典數據result中statuCode字段的值爲"000000" if result.get("statusCode") == "000000": # 返回0 表示發送短信成功 return 0 else: # 返回-1 表示發送失敗 return -1 if __name__ == '__main__': ccp = CCP() # 注意: 測試的短信模板編號爲1[之後申請了企業帳號之後能夠有更多的模板] # 參數1: 客戶端手機號碼,測試時只能發給測試號碼 # 參數2: 短信模塊中的數據 # 短信驗證碼 # 短信驗證碼有效期提示 # 參數3: 短信模板的id,開發測試時,只能使用1 result = ccp.send_template_sms('13928835901', ['1234',5], 1) print(result)
配置文件,代碼:
# 短信配置 # 主帳號 SMS_ACCOUNTSID = '8a216da86ab0b4d2016ab3e05fe400b1' # 主帳號Token SMS_ACCOUNTTOKEN = '5f0ba4296bbb4e248aa77253ccfe0b31' # 建立應用的APPID SMS_APPID = '8a216da86ab0b4d2016ab3e0603900b7' # 說明:請求地址,生產環境配置成app.cloopen.com SMS_SERVERIP = 'sandboxapp.cloopen.com'
from luffy.libs.yuntongxun.sms import CCP from django_redis import get_redis_connection class SMSAPIView(APIView): # url: users/sms/(?P<mobile>1[3-9]\d{9}) def get(self,request,mobile): ccp = CCP() sms_code = "%04d" % random.randint(1,9999) result = ccp.send_template_sms(mobile,[sms_code, 5],1) if not result: """發送成功""" redis = get_redis_connection("sms_code") redis.setex("%s_sms_code" % mobile, 5*60, sms_code) return Response({"result":result})
urls.py,代碼:
re_path(r'sms/(?P<mobile>1[3-9]\d{9})/', views.SMSAPIView.as_view() ),
調整前端的頁面,添加一個發送短信功能,
html代碼:
<div class="sms-box"> <input v-model = "sms" type="text" placeholder="輸入驗證碼" class="user"> <div class="sms-btn" @click="smsHandle">點擊發送短信</div> </div>
css,代碼:
.sms-box{ position: relative; } .sms-btn{ font-size: 14px; color: #ffc210; letter-spacing: .26px; position: absolute; right: 16px; top: 10px; cursor: pointer; overflow: hidden; background: #fff; border-left: 1px solid #484848; padding-left: 16px; padding-bottom: 4px; }
script,代碼:
data裏面的methods中代碼:
methods:{ // 發送短信 smsHandle() { // 判斷是否填寫了手機 if( !/^\d{11}$/.test(this.mobile) ){ this.$alert('手機號碼格式有誤!', '警告'); return false; } this.$axios.get(this.$settings.Host+`/users/sms/${this.mobile}/`).then(response=>{ let data = response.data if( data.result == '-1' ){ this.$alert("發送短信失敗!","錯誤"); }else{ this.$alert("發送短信成功了!","成功"); } }).catch(error=>{ console.log(error.response) }) }, // 提交註冊信息 ....
<template> .... <div class="sms-box"> <input v-model = "sms" type="text" placeholder="輸入驗證碼" class="user"> <div class="sms-btn" @click="smsHandle">{{sms_text}}</div> </div> .... </template> <script> export default { name: 'Register', data(){ return { sms:"", mobile:"", password:"", password2:"", validateResult:false, is_send:false, // 是否已經發送短信的狀態 send_intervel:60, // 發送短信的間隔 sms_text:"點擊發送短信", // 發送短信的提示 } }, methods:{ // 發送短信 smsHandle() { // 判斷是否填寫了手機 if( !/^\d{11}$/.test(this.mobile) ){ this.$alert('手機號碼格式有誤!', '警告'); return false; } // 判斷是否在60s內有發送太短信,若是有則,不能點擊發送 if(this.is_send){ this.$alert('60s內不能頻繁發送短信!', '警告'); return false; } let _this = this; _this.$axios.get(_this.$settings.Host+`/users/sms/${_this.mobile}/`).then(response=>{ let data = response.data; if( data.result == '-1' ){ _this.$alert("發送短信失敗!","錯誤"); }else{ _this.is_send = true; _this.$alert("發送短信成功了!","成功",{ callback(){ let num = _this.send_intervel let timer = setInterval(()=>{ if(num<1){ clearInterval(timer); _this.sms_text = "點擊發送短信"; _this.is_send = false; }else{ num--; _this.sms_text = num+"後可繼續點擊發送"; } },1000) } }); } }).catch(error=>{ console.log(error.response) }) }, // 提交註冊信息 registerHander(){ .... } }, }; </script>
視圖代碼:
class SMSAPIView(APIView): # url: users/sms/(?P<mobile>1[3-9]\d{9}) def get(self,request,mobile): redis = get_redis_connection("sms_code") # 獲取短信發送間隔 try: interval = redis.get("%s_interval" % mobile) if interval: print(interval) return Response({"result":"-1"}) except: pass ccp = CCP() sms_code = "%04d" % random.randint(1,9999) result = ccp.send_template_sms(mobile,[sms_code, 5],1) if not result: """發送成功""" redis.setex("%s_sms_code" % mobile, 5*60, sms_code) # 這裏的值不重要,重要的是這個變量是否在redis被查找到 redis.setex("%s_interval" % mobile, 60, 1) return Response({"result":result})
建立序列化器對象[暫時不涉及到手機驗證碼功能]
from rest_framework import serializers from .models import User import re from django_redis import get_redis_connection class UserModelSerializer(serializers.ModelSerializer): sms_code = serializers.CharField(write_only=True, max_length=4,min_length=4,required=True,help_text="短信驗證碼") password2 = serializers.CharField(write_only=True,help_text="確認密碼") token = serializers.CharField(read_only=True,help_text="jwt token值") class Meta: model = User fields = ["mobile","id","token","password","password2","username","sms_code"] extra_kwargs = { "id":{"read_only":True}, "username":{"read_only":True}, "password":{"write_only":True}, "mobile":{"write_only":True} } def validate_mobile(self, mobile): # 驗證格式 result = re.match('^1[3-9]\d{9}$', mobile) if not result: raise serializers.ValidationError("手機號碼格式有誤!") # 驗證惟一性 try: user = User.objects.get(mobile=mobile) if user: raise serializers.ValidationError("當前手機號碼已經被註冊!") except User.DoesNotExist: pass return mobile def validate(self, attrs): # 判斷密碼長度 password = attrs.get("password") if not re.match('^.{6,16}$', password): raise serializers.ValidationError("密碼長度必須在6-16位之間!") # 判斷密碼和確認密碼是否一致 password2 = attrs.get("password2") if password != password2: raise serializers.ValidationError("密碼和確認密碼不一致!") # 驗證短信驗證碼 mobile = attrs.get("mobile") redis = get_redis_connection("sms_code") try: real_sms_code = redis.get("%s_sms_code" % mobile).decode() except: raise serializers.ValidationError("驗證碼不存在,或已通過期!") if real_sms_code != attrs.get("sms_code"): raise serializers.ValidationError("驗證碼不存在,或錯誤!") # 刪除本次使用的驗證碼 try: redis.delete("%s_sms_code" % mobile) except: pass return attrs def create(self, validated_data): """保存用戶""" mobile = validated_data.get("mobile") password = validated_data.get("password") try: user = User.objects.create( mobile=mobile, username=mobile, password=password, ) # 密碼加密 user.set_password(user.password) user.save() except: raise serializers.ValidationError("註冊用戶失敗!") # 生成一個jwt from rest_framework_jwt.settings import api_settings jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) user.token = jwt_encode_handler(payload) return user
視圖代碼:
from .serializers import UserModelSerializer from rest_framework.generics import CreateAPIView from .models import User class UserAPIView(CreateAPIView): serializer_class = UserModelSerializer queryset = User.objects.all()
# 子應用路由 urls.py urlpatterns=[ ... path(r'register/', views.UserAPIView.as_view() ), ]
客戶端發送註冊信息時附帶發送短信