文章目录
腾讯云地址 https://console.cloud.tencent.com/smsv2[腾讯云短信地址](https://console.cloud.tencent.com/smsv2)
腾讯云SDK
python
# -*- coding: utf-8 -*-
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
# 导入对应产品模块的client models。
from tencentcloud.sms.v20210111 import sms_client, models
# 导入可选配置类
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
try:
# SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi
cred = credential.Credential("T0gF0iU8gJ5XEvW0cLyWzZME97yQipya", "gS0X8vlkfkyKmJzgO4kKlm2OuW4Wug") # 必填 这里是一对密钥 一个key 一个value
# 实例化一个http选项,可选的,没有特殊需求可以跳过。
httpProfile = HttpProfile()
# 如果需要指定proxy访问接口,可以按照如下方式初始化hp(无需要直接忽略)
# httpProfile = HttpProfile(proxy="http://用户名:密码@代理IP:代理端口")
httpProfile.reqMethod = "POST" # post请求(默认为post请求)
httpProfile.reqTimeout = 300 # 请求超时时间,单位为秒(默认60秒)
httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入)
clientProfile = ClientProfile()
clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法
clientProfile.language = "en-US"
clientProfile.httpProfile = httpProfile
client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile) # 支持地域信息
req = models.SendSmsRequest()
# 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
req.SmsSdkAppId = "14008852" # 必填
# 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名
req.SignName = "蟒蛇梦想家公众号" # 必填
# 模板 ID: 必须填写已审核通过的模板 ID
req.TemplateId = "20579" # 必填
req.TemplateParamSet = ["520"] # 必填 # 短信显示的内容 里面有两个参数 比如验证码和过期和时间
# 您正在申请手机注册,验证码为:{1},{2}分钟内有效,打死都不要告诉比人!
req.PhoneNumberSet = ["+86"] # 必填 # 需要发送短信的客户
# 不用填
req.SessionContext = ""
req.ExtendCode = ""
req.SenderId = ""
resp = client.SendSms(req)
print(resp.to_json_string(indent=2))
except TencentCloudSDKException as err:
print(err)
短信封装
python
# 创建一个文件
import random
import json
from rest_framework.exceptions import APIException
# 随机生成数字 传入几个验证码就是几位
def get_code(length):
code = ''
for i in range(length):
code += str(random.randint(0, 9))
return code
# -*- coding: utf-8 -*-
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
# 导入对应产品模块的client models。
from tencentcloud.sms.v20210111 import sms_client, models
from . import settings
# 导入可选配置类
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
def common_send_sms(code, mobile):
try:
# SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi
cred = credential.Credential(settings.SECRET_ID, settings.SECRET_KEY) # 必填 这里是一对密钥 一个key 一个value
# 实例化一个http选项,可选的,没有特殊需求可以跳过。
httpProfile = HttpProfile()
# 如果需要指定proxy访问接口,可以按照如下方式初始化hp(无需要直接忽略)
# httpProfile = HttpProfile(proxy="http://用户名:密码@代理IP:代理端口")
httpProfile.reqMethod = "POST" # post请求(默认为post请求)
httpProfile.reqTimeout = 300 # 请求超时时间,单位为秒(默认60秒)
httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入)
clientProfile = ClientProfile()
clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法
clientProfile.language = "en-US"
clientProfile.httpProfile = httpProfile
client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile) # 支持地域信息
req = models.SendSmsRequest()
# 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
req.SmsSdkAppId = settings.APP_ID # 必填
# 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名
req.SignName = settings.SIGN # 必填
# 模板 ID: 必须填写已审核通过的模板 ID
req.TemplateId = settings.TEMPLATE_ID # 必填
req.TemplateParamSet = [code] # 必填 # 短信显示的内容 里面有两个参数 比如验证码和过期和时间
# 您正在申请手机注册,验证码为:{1},{2}分钟内有效,打死都不要告诉比人!
req.PhoneNumberSet = ["+86" + str(mobile)] # 必填 # 需要发送短信的客户
# 不用填
req.SessionContext = ""
req.ExtendCode = ""
req.SenderId = ""
resp = client.SendSms(req)
# 输出json格式的字符串回包
resp = json.loads(resp.to_json_string(indent=2))
if resp.get('SendStatusSet')[0].get('Code') == 'Ok':
return True
else:
return False
except Exception as e:
raise APIException(detail=str(e))
# 自定义配置文件
SECRET_ID = "KIDT0gF0iU8gJ5XEvW0cLyWzZME97yQipya"
SECRET_KEY = "JsS0X8vlkfkyKmJzgO4kKlm2OuW4Wug"
# 申请的短信应用 SDK AppID
APP_ID = '14885290'
# 申请的短信模板ID,需要在短信控制台中申请
TEMPLATE_ID = '057994'
# 申请的签名,参数使用的是`签名内容`,而不是`签名ID`
SIGN = "蟒蛇梦想家公众号"
# 视图类:
# 发送短信接口
from rest_framework.viewsets import ViewSet,GenericViewSet
from rest_framework.decorators import action
from utils.common_response import APIResponse # 自定义返回格式
from rest_framework.exceptions import APIException # 全局异常捕获
# 自定义Response
# 必须要继承 Response
from rest_framework.response import Response
class APIResponse(Response):
# 重写父类的属性 data可以写自己规定的自定义状态码
def __init__(self,code=100, msg='成功', status=None,headers=None,**kwargs):
data = {'code':code,'msg':msg}
# 判断一下还有没有返回其他需要返回的数据 字典里面有就更新 没有就创建
if kwargs:
data.update(kwargs)
super().__init__(data=data,headers=headers,status=status)
class SmsView(GenericViewSet):
@action(methods=['POST'], detail=False)
def send(self, request):
try:
mobile = request.data['mobile']
code = get_code(6) # 生成验证码,要存一下 之前验证码放session中,现在放在缓存中
cache.set('sms_code_%s' % mobile, code) # 放在缓存中,以手机号做区分
res = common_send_sms(code, mobile) # 同步发送
if res:
return APIResponse(msg='短信发送成功')
else:
return APIResponse(code=101, msg='短信发送失败')
# 异步发送短信---不用管是否成功---》如果不成功,用户在发一次即可
# from threading import Thread
# t = Thread(target=common_send_sms, args=[code, mobile])
# t.start()
# return APIResponse(msg='短信已发送')
except MultiValueDictKeyError:
raise APIException(detail='手机号必须携带')
except Exception as e:
raise APIException(detail=str(e))
短信登录功能
python
# 视图类
class UserLoginView(GenericViewSet):
@action(methods=['POST'], detail=False)
def sms_login(self, request):
return self._common_login(request)
def get_serializer_class(self):
if self.action == 'sms_login':
return SmsLoginSerializer
else:
return super().get_serializer_class()
def _common_login(self, request):
ser = self.get_serializer(data=request.data, context={'request': request})
ser.is_valid(raise_exception=True)
token = ser.context.get('token')
username = ser.context.get('username')
icon = ser.context.get('icon')
return APIResponse(token=token, username=username, icon=icon)
# 序列化类
class CommonLoginSerializer(serializers.Serializer):
def validate(self, attrs):
user = self._get_user(attrs)
token = self._get_token(user)
self._set_context(token, user)
return attrs
def _get_user(self, attrs):
raise Exception('这个方法必须被重写')
def _get_token(self, user):
refresh = RefreshToken.for_user(user)
# self.context['refresh'] = str(refresh)
return str(refresh.access_token)
def _set_context(self, token, user):
request = self.context.get('request')
# print(request.headers.get('Host'))
self.context['token'] = token
self.context['username'] = user.username
# icon 缺了前面 http://127.0.0.1:8000/media/
# 如果从request中取不出来服务端ip和端口
# 把ip和端口配置在配置文件中
# self.context['icon'] = request.META.get('HTTP_HOST')+'/media/'+str(user.icon) # user.icon 文件对象
self.context['icon'] = settings.BACKEND_URL + '/media/' + str(user.icon) # user.icon 文件对象
# 只做 反序列化的校验,其他不用
class SmsLoginSerializer(CommonLoginSerializer):
mobile = serializers.CharField()
code = serializers.CharField()
def _get_user(self, attrs):
# 1 取出验证码
code = attrs.get('code')
mobile = attrs.get('mobile')
old_code = cache.get('sms_code_%s' % mobile)
# 2 校验验证码是否正确,不正确,抛异常
# 留了个后门,为了测试方便,不再真正发送验证码
if code == old_code or (settings.DEBUG and code == '8888'):
# 3 拿着手机号查询用户,查不到用户,抛异常
user = User.objects.filter(mobile=mobile).first()
if user:
return user
# 4 返回用户即可
else:
raise APIException('用户不存在')
else:
raise APIException('验证码错误')
注册功能
python
# 视图类
# 注册功能
class UserRegister(GenericViewSet):
serializer_class = RegisterSerializer
# 两种方法都可以 http://127.0.0.1:8000/api/v1/user/register/
# @action(methods=['POST'], detail=False)
# def register(self, request):
# ser = self.get_serializer(data=request.data)
# ser.is_valid(raise_exception=True)
# ser.save() # 走序列化类的create方法
# return APIResponse(msg='恭喜您,注册成功')
def create(self, request):
ser = self.get_serializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save() # 走序列化类的create方法
return APIResponse(msg='恭喜您,注册成功')
# 路由 http://127.0.0.1:8000/api/v1/user/ post提交
# 序列化类
class RegisterSerializer(serializers.ModelSerializer):
code = serializers.CharField() # 因为code不是表的字段,所以必须重写
class Meta:
model = User
fields = ['mobile', 'code', 'password']
def validate(self, attrs):
# 1 校验验证码是否正确
code = attrs.pop('code')
mobile = attrs.get('mobile')
old_code = cache.get('sms_code_%s' % mobile)
# 2 注册前的数据准备(用户名)
if code == old_code or (settings.DEBUG and code == '6666'):
# 2.1 code pop 出来 上面做了
# 2.2 把用户名放入
attrs['username'] = mobile
else:
raise APIException('验证码错误')
# 3 返回校验过后的数据
return attrs
def create(self, validated_data):
# validated_data {mobile,password,username}
user = User.objects.create_user(**validated_data)
return user