day80 短信功能 多方式登录

短信功能

腾讯云短信服务

"""
0、注册微信公众号平台
1、注册开发者账号
2、创建短信应用
3、创建签名与模板
"""

短信功能不封装

导入腾讯云短信接口pip install qcloudsms_py

# 短信应用 SDK AppID
appid = 1400303014  # SDK AppID 以1400开头
# 短信应用 SDK AppKey
appkey = "fe256e50a2d3e8d3cd07f21c42802ad3"

# 短信模板ID,需要在短信控制台中申请
template_id = 517040  # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
# 签名,参数使用的是`签名内容`
sms_sign = "小阿峰啊公众号"  # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请

from qcloudsms_py import SmsSingleSender
sender = SmsSingleSender(appid, appkey)
import random
def get_code():
    code=''
    for i in range(6):
        code+=str(random.randint(0,9))
    return code

code=get_code()
print(code)
# params = ["5678", 5]  # 当模板没有参数时,`params = []`
# 需要发送短信的手机号码
mobile = "17356530633"
try:
    response = sender.send_with_param(86, mobile,template_id,(code,5) , sign=sms_sign, extend="", ext="")
    if response and response.get('result')==0:
        print('ok')
    print(response)
except Exception as e:
    print(e)

短信功能的二次封装

libs/tx_sms/settings.py

# 短信应用 SDK AppID
APP_ID = 1400303014  # SDK AppID 以1400开头

# 短信应用 SDK AppKey
APP_KEY = "fe256e50a2d3e8d3cd07f21c42802ad3"

# 短信模板ID,需要在短信控制台中申请
TEMPLATE_ID = 517040  # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请

# 签名,参数使用的是`签名内容`
SMS_SIGN = "小阿峰啊公众号"  # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请

libs/tx_sms/sms.py

import random


# 获取六位数字验证码
def get_sms_code():
    code = ''
    for i in range(6):
        code += str(random.randint(0, 9))
    return code


from qcloudsms_py import SmsSingleSender
from .sesttings import APP_ID, APP_KEY, TEMPLATE_ID, SMS_SIGN

sender = SmsSingleSender(APP_ID, APP_KEY)
from utils.logging import logger


def send_sms(mobile, code, exp):
    try:
        response = sender.send_with_param(86, mobile, TEMPLATE_ID, (code, exp), sign=SMS_SIGN, extend="", ext="")
        if response and response.get('result') == 0:
            return True
        logger.error('短信发送失败,状态码:%s,错误信息:%s' % (response.get('result'), response.get('errmsg')))
    except Exception as e:
        logger.error('短信发送异常,异常信息:%s' % e)
    return False

libs/tx_sms/__init__.py

from .sms import get_sms_code,send_sms

调用

from libs import tx_sms
code=tx_sms.get_sms_code()
print(code)
result=tx_sms.send_sms('17356530633',code,5)
if result:
    print('发送成功') 

多方式登录

视图层

import re
from rest_framework.views import APIView
from . import serializers
from utils.response import APIResponse

# Create your views here.

# 多方式登录
class LoginAPIView(APIView):
    #认证权限一定要局部禁用
    authentication_classes = ()
    permission_classes = ()

    def post(self,request,*args,**kwargs):
        serializer=serializers.LoginModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)# 内部在全局钩子中完成token的签发
        return APIResponse(results={
            'username':serializer.content.get('user').username,
            'token':serializer.content.get('token')
        })

序列化

import re
from rest_framework import serializers
from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
from . import models
class LoginModelSerializer(serializers.ModelSerializer):
    #post请求,序列化默认当做create动作进行校验,需要校验数据库,create动作username会抛用户已存在异常
    #抛用户已存在异常是多余的,所有自定义系统校验规则即可

    username=serializers.CharField(min_length=3,max_length=16)
    password=serializers.CharField(min_length=3,max_length=16)

    class Meta:
        model=models.User
        fields=('username','password')

    #用全局钩子,完成token的签发
    def validate(self, attrs):
        #通过username和password完成多方式登录校验,得到user对象
        user=self._validata_user(attrs)
        #user对象包装payload载荷
        payload=jwt_payload_handler(user)
        #payload载荷签发token
        token=jwt_encode_handler(payload)
        #将user与token存储到serilizer对象中,方便在视图类中使用
        self.content={
            'user':user,
            'token':token
        }
        return attrs

    def _validata_user(self,attr):
        username=attr.get('username')
        password=attr.get('password')

        if re.match(r'.*@.*',username):#邮箱
            user=models.User.objects.filter(email=username).first()
        elif re.match(r'^1[3-9][0-9]{9}$',username):#电话
            user=models.User.objects.filter(mobile=username).first()
        else:#用户名
            user=models.User.objects.filter(username=username).first()

        if not user or not user.check_password(password):
            raise serializers.ValidationError({'message':'用户信息异常'})
        return user

验证码发送

视图层

#发送验证码
from libs import tx_sms
from django.core.cache import cache
from django.conf import settings

class SMSAPIView(APIView):
    authentication_classes = ()
    permission_classes = ()

    def post(self, request, *args, **kwargs):
        #处理手机号
        mobile=request.data.get('mobile',None)
        if not mobile:
            return APIResponse(1, msg='mobile字段必须', http_status=400)

        if not re.match(r'^1[3-9][0-9]{9}$',mobile):
            return APIResponse(1, msg='mobile格式有误', http_status=400)

        #生成验证码
        code=tx_sms.get_sms_code()

        #发送验证码
        result=tx_sms.send_sms(mobile,code,settings.SMS_EXP//60)

        if not result:
            return APIResponse(1, msg='验证码发送失败')

        #缓存验证码
        print(settings.SMS_EXP)
        cache.set(settings.SMS_CACHE_FORMAT % mobile,code,settings.SMS_EXP)

        #成功响应
        return APIResponse(0,msg='验证码发送成功')

const.py

# 短信验证码缓存key
SMS_CACHE_FORMAT = 'sms_cache_%s'

# 短信过期时间s
SMS_EXP = 3000000

手机登录

视图层

# 手机验证码登录
class LoginMobileAPIView(APIView):
    # 认证权限一定要局部禁用
    authentication_classes = ()
    permission_classes = ()

    def post(self, request, *args, **kwargs):
        serializer = serializers.LoginMobileModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        return APIResponse(results={
            'username': serializer.content.get('user').username,
            'token': serializer.content.get('token')
        })

序列化

from django.core.cache import cache
from django.conf import settings
class LoginMobileModelSerializer(serializers.ModelSerializer):
    mobile=serializers.CharField(min_length=11,max_length=11)
    code=serializers.CharField(min_length=6,max_length=6)

    class Meta:
        model=models.User
        fields=('mobile','code')

    #验证码格式内容有误就不需要进行 取服务器存储的验证码(IO操作)进行校验
    def validate_code(self,value):
        try:
            int(value)
            return value
        except:
            raise serializers.ValidationError('验证码有误')

    def validate(self, attrs):
        mobile=attrs.get('mobile')
        code=attrs.pop('code')
        #那服务器缓存的验证码
        old_code=cache.get(settings.SMS_CACHE_FORMAT % mobile)

        if code!=old_code:
            raise serializers.ValidationError({'code': '验证码有误'})

        try:
            user = models.User.objects.get(mobile=mobile, is_active=True)
        except:
            raise serializers.ValidationError({'mobile': '该用户不存在'})

        payload=jwt_payload_handler(user)
        token=jwt_encode_handler(payload)
        self.content = {
            'user': user,
            'token': token
        }
        return attrs

你可能感兴趣的:(day80 短信功能 多方式登录)