實現手機註冊——使用Python(DRF應用)接通騰訊雲簡訊服務介面
本篇以註冊或者登陸的單次單人傳送場景為例,使用Python進行簡訊介面的除錯,從而實現DRF應用的使用者註冊功能,至於群發或者語音等簡訊的場景,合此類似,本篇不做完整介紹。
一、申請騰訊雲簽名並建立模板
首先在騰訊雲服務中找到簡訊服務,點選開通,填寫基本資訊,最後生成如下:
其中SDK AppID和App key是在https請求傳送是的必要引數。
然後需要建立一個簽名,簽名相當於是簡訊服務的標題,提交後大概稽核1-2個小時就可以了。我這裡申請的是小程式的服務,所以只需要提供小程式的後臺截圖就可以了。
要注意的是簽名的內容必須和小程式或者公眾號的名稱一樣,不然會稽核不通過。
建立成功以後就會顯示已通過,並且建立簽名的ID
同理再建立簡訊正文的模板:
我們可以看到在正文的內容當中,我設定了兩個變數{1}和{2},所以在接下來的傳參過程中,僅正文內容的傳參就需要兩個,另外這裡要注意正文模板的ID號,待會在傳送請求的過程中也是需要的。
二、建立並封裝Python簡訊請求檔案tengxun.py
首先我們還是在我們的專案中建立一個tengxun.py檔案用於封裝簡訊傳送的程式碼。
以我的專案為例,我使用的DRF,將這個檔案放在了app目錄下的utils資料夾中。
為了方便進行請求,我們使用的是騰訊簡訊的SDK,對於Python而言,可以直接使用pip安裝:
pip install qcloudsms_py
如下圖所示安裝成功:
對於單條簡訊的傳送有其特定的傳送引數,至於其他型別的傳送,大家可以參考騰訊官方的SDK文件,託管到了Github上,所以我直接給出Github的地址,下發都是有說明的:
如下建立tengxun.py檔案:
#!/usr/bin/env python
# encoding: utf-8
'''
@author: ZhonghangAlex
@contact: [email protected]
@software: pycharm
@file: tengxun.py
@time: 2018/11/28 12:43
@desc:
'''
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
class TengXun(object):
def __init__(self, appkey):
self.appkey = appkey
def send_sms(self, code, mobile):
# 簡訊應用SDK AppID
appid = 1400xx3806 # SDK AppID是1400開頭
# 簡訊應用SDK AppKey
appkey = self.appkey
# 需要傳送簡訊的手機號碼
phone_numbers = "{mobile}".format(mobile=mobile)
# 簡訊模板ID,需要在簡訊應用中申請
template_id = 2393xx # NOTE: 這裡的模板ID`7839`只是一個示例,真實的模板ID需要在簡訊控制檯中申請
# 簽名
sms_sign = "Enet車聯" # NOTE: 這裡的簽名"騰訊雲"只是一個示例,真實的簽名需要在簡訊控制檯中申請,另外簽名引數使用的是`簽名內容`,而不是`簽名ID`
ssender = SmsSingleSender(appid, appkey)
params = ["{code}".format(code=code),"3"] # 當模板沒有引數時,`params = []`
try:
result = ssender.send_with_param(86, phone_numbers, template_id, params, sign=sms_sign, extend="", ext="") # 簽名引數未提供或者為空時,會使用預設簽名傳送簡訊
except HTTPError as e:
print(e)
except Exception as e:
print(e)
print(result)
return result
# 指令碼自測
if __name__ == "__main__":
teng_xun = TengXun("a067bbcc3xx1c4b924xxc04bef7dc926")
teng_xun.send_sms("2018", "15927093114")
上面的很多引數,我都使用了xx做了替換,大家在寫的過程中填寫自己的相關引數就好。
可以看到在程式碼中我們封裝了一個Tengxun類用於接收引數以及進行簡訊的傳送。
程式碼下方的三行是指令碼自測,先例項化,傳入appkey,2018是要生產的驗證碼,15927093114是要傳送的手機號。
這個時候直接執行這個檔案就可以完成對簡訊的傳送。
可以看到返回的引數:
說明發送成功,此時手機上也接收到了簡訊:
如果你僅僅是想使用一個指令碼進行簡訊傳送那麼就完成了,接下來我會結合DRF的相關操作,實現一個完整的使用者註冊功能。
三、DRF實現使用者註冊
為了程式碼的標準化,我們將一些全域性的引數放在settings中:
# 手機號碼正則表示式
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
# 騰訊簡訊設定
APIKEY = "a067bbcxx1f1c4b924xxc04bef7dc926"
由於註冊功能面向的是使用者,所以我們就在user這個app中進行實現:
首先我們要建立簡訊驗證的資料庫表,在models.py中編輯如下:
from datetime import datetime
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class VerifyCode(models.Model):
"""
簡訊驗證
"""
code = models.CharField(max_length=10, verbose_name="驗證碼")
mobile = models.CharField(max_length=11, verbose_name="電話")
add_time = models.DateTimeField(default=datetime.now, verbose_name="新增時間")
class Meta:
verbose_name = "簡訊驗證碼"
verbose_name_plural = verbose_name
def __str__(self):
return self.code
接著在serserializer.py中設定序列化格式,並進行檢驗:
#!/usr/bin/env python
# encoding: utf-8
'''
@author: ZhonghangAlex
@contact: [email protected]
@software: pycharm
@file: serializer.py
@time: 2018/11/28 13:15
@desc:
'''
import re
from rest_framework import serializers
from django.contrib.auth import get_user_model
from datetime import datetime
from datetime import timedelta
from .models import VerifyCode
from VueShop.settings import REGEX_MOBILE
User = get_user_model()
class SmsSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=11)
def validate_mobile(self, mobile):
"""
驗證手機號碼
:param data:
:return:
"""
# 手機是否註冊
if User.objects.filter(mobile=mobile).count() != 0:
raise serializers.ValidationError("使用者已經存在")
# 驗證手機號碼是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手機號碼非法")
# 驗證傳送頻率
one_min_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
if VerifyCode.objects.filter(add_time__gt=one_min_ago, mobile=mobile).count():
raise serializers.ValidationError("距離上一次傳送未超過60s")
return mobile
接下來就要構建views.py的程式碼,我們在Tengxun.py檔案中封裝類TengXun這個類,所以我們可以在view中進行呼叫,其中用於單條簡訊傳送的class如下(採用CBV方式):
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from random import choice
from rest_framework.mixins import CreateModelMixin
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import status
from VueShop.settings import APIKEY
from .serializer import SmsSerializer
from utils.tengxun import TengXun
from .models import VerifyCode
class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):
"""
傳送簡訊驗證碼
"""
serializer_class = SmsSerializer
def generate_code(self):
"""
生成四位數字的驗證碼
:return:
"""
seeds = "1234567890"
random_str = []
for i in range(4):
random_str.append(choice(seeds))
return "".join(random_str)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
mobile = serializer.validated_data["mobile"]
teng_xun = TengXun(APIKEY)
code = self.generate_code()
sms_status = teng_xun.send_sms(code=code, mobile=mobile)
if sms_status["result"] != 0:
return Response({
"mobile":sms_status["errmsg"]
}, status=status.HTTP_400_BAD_REQUEST)
else:
code_record = VerifyCode(code=code, mobile=mobile)
code_record.save()
return Response({
"mobile":mobile
}, status=status.HTTP_201_CREATED)
然後在url配置中加入code配置:
# 配置code的url
router.register(r'code', SmsCodeViewset, base_name="code")
就完成了DRF應用對簡訊介面的呼叫,生成了如下的API:
最後要進行的就是登錄檔單的驗證
在serializer.py中新增類
class UserRegSerializer(serializers.ModelSerializer):
code = serializers.CharField(required=True, write_only=True, allow_blank=False, max_length=4, min_length=4,label="驗證碼",
error_messages={
"blank":"請輸入驗證碼",
"required":"請輸入驗證碼",
"max_length":"驗證碼格式錯誤",
"min_length":"驗證碼格式錯誤"
}, help_text="驗證碼")
username = serializers.CharField(required=True, allow_blank=False, label="使用者名稱", validators=[UniqueValidator(queryset=User.objects.all(), message="使用者已經存在")])
password = serializers.CharField(
write_only=True,
style={'input_type':'password'},
label="密碼"
)
# 二次加密密碼,儲存資料庫時生成不能反解的密碼
def create(self, validated_data):
user = super(UserRegSerializer, self).create(validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
return user
def validate_code(self, code):
# 為什麼不使用get方法
# 因為使用get方法需要做異常的捕獲
# try:
# verify_records = VerifyCode.objects.get(mobile=self.initial_data["userame"], code=code)
# except VerifyCode.DoesNotExist as e:
# return e
# except VerifyCode.MultipleObjectsReturned as e:
# return e
verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
if verify_records:
last_records = verify_records[0]
three_min_ago = datetime.now() - timedelta(hours=0, minutes=3, seconds=0)
if three_min_ago > last_records.add_time:
raise serializers.ValidationError("驗證碼過期")
if last_records.code != code:
raise serializers.ValidationError("驗證碼錯誤")
else:
raise serializers.ValidationError("驗證碼錯誤")
def validate(self, attrs):
attrs["mobile"] = attrs["username"]
del attrs["code"]
return attrs
class Meta:
model = User
fields = ("username", "code", "mobile", "password")
在View中新增檢視類,並且通過重寫create和perform_create實現token定製化,實現註冊後自動登入:
class UserViewSet(CreateModelMixin, viewsets.GenericViewSet):
"""
使用者
"""
serializer_class = UserRegSerializer
queryset = User.objects.all()
# 重新定義create函式 實現註冊後自動登入 及定製化Token
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer)
re_dict = serializer.data
payload = jwt_payload_handler(user)
re_dict["token"] = jwt_encode_handler(payload)
re_dict["name"] = user.name if user.name else user.username
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
return serializer.save()
同樣在urls.py中進行路由的配置:
# 配置user的url
router.register(r'user', UserViewSet, base_name="users")
這樣以來就完整地實現了通過簡訊註冊的邏輯。