Django项目--路飞学城项目05


1 腾讯云短信二次封装

tx_sms
    __init__.py
    settings.py
    sms.py

1.1 init.py

from .sms import get_code, send_sms

1.2 settings.py

# 短信应用 SDK AppID
APPPID =   # SDK AppID 以1400开头
# 短信应用 SDK AppKey
APPKEY = ""

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

1.3 sms.py

import random
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
from .settings import *
from utils.logging import get_logger

logger = get_logger()

# 获取验证码的函数(4位数字)
def get_code():
    code_str = ''
    for i in range(4):
        number = str(random.randint(0, 9))
        code_str += number
    return code_str


# 发送短信的函数
def send_sms(phone, code):
    ssender = SmsSingleSender(APPPID, APPKEY)
    params = [code, '1']  # 当模板没有参数时,`params = []`
    try:
        result = ssender.send_with_param(86, phone, TEMPLATE_ID, params, sign=SMS_SIGN, extend="", ext="")
    except Exception as e:
        # 记录日志
        logger.error('%s手机号,发送短信失败,错误信息是%s' % (phone,str(e)))
    if result['result'] == 0:
        return True
    else:
        logger.warning('%s手机号,发送短信失败,失败原因是%s' % (phone, result['errmsg']))
        return False

2 短信发送接口

# 前端给我手机号,get请求

2.1 视图

# action装饰器的拓展使用--给同一个视图view下,不同的接口,添加不同的类设置
	可用action装饰器 参数:throttle_classes=[SMSThrottle]

class MobileView(ViewSet):
    @action(methods=['GET'],detail=False,url_path='sms',throttle_classes=[SMSThrottle])
    def send_sms(self, request):
        mobile = request.query_params.get('mobile')
        if mobile and re.match('^1[3-9][0-9]{9}$', mobile):
            # 发送短信
            # 获取验证码,记录验证码(放到缓存中,默认还是缓存到内存)
            code = tx_sms.get_code()
            cache.set(settings.CACHE_SMS % mobile, code)

            # 调用发送短信函数
            res = tx_sms.send_sms(mobile, code)
            if res:
                return APIResponse(msg='短信发送成功')
            else:
                raise APIException('短信发送失败')
        else:
            raise APIException('手机号不合法')

2.2 路由

# view 采用的自动生成路由

# 这是自己写路由的形式
path('sms/', views.LoginView.as_view({'get':'send_sms'})), 

2.3 配合一个频率类

# 限制一个手机号,一分钟只能发一次验证码

# 频率类全局配置
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'sms': '1/m',
    },
}
from rest_framework.throttling import SimpleRateThrottle
class SMSThrottle(SimpleRateThrottle):
    scope = 'sms'

    def get_cache_key(self, request, view):
        mobile = request.query_params.get('mobile')
        return mobile

3 短信登录接口

# post, 手机号,验证码

3.1 视图

class LoginView(ViewSet):
    def mobile_login(self, request):
        # 逻辑写在序列化类中
        ser = serializer.MobileLoginSerializer(data=request.data)

        ser.is_valid(raise_exception=True)  # 执行序列化类字段自己的校验规则,局部钩子,全局钩子

        token = ser.context.get('token')
        username = ser.context.get('username')
        return APIResponse(msg='登录成功', username=username, token=token)

3.2 序列化类

class MobileLoginSerializer(serializers.ModelSerializer):
    # 重写它的原因,unique=True
    mobile = serializers.CharField()
    #code 由于不是表的字段,所有必须重写
    code=serializers.CharField()
    class Meta:
        model = models.User
        fields = ['code', 'mobile']

    # 用户校验和签发token
    def validate(self, attrs):
        '''
        1 校验code是否正确:从缓存中根据手机号获取code,比较
        2 根据手机号,拿到当前用户,
        3 根据用户,签发token
        4 把token放到序列化类的对象中
        '''

        user = self._get_user(attrs)
        token = self._get_token(user)
        # 上下文,是个字典
        self.context['token'] = token
        self.context['username'] = user.username

        return attrs

    def _get_user(self, attrs):  # 不是私有,但尽量只给内部用
        code = attrs.get('code')
        mobile = attrs.get('mobile')
        # 校验code
        cache_code = cache.get(settings.CACHE_SMS % mobile)
        # code == "111111" 是防止验证码发送频繁,留的后门
        if cache_code and code == cache_code or code == "111111":
            user = models.User.objects.filter(mobile=mobile).first()
            if not user:
                raise ValidationError({'detail': '手机号不存在'})
            return user
        else:
            raise ValidationError({'detail': '验证码错误'})

    def _get_token(self, user):
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token

3.3 路由

path('mobile_login/', views.LoginView.as_view({'post':'mobile_login'})),

4 短信注册接口

# 手机号  验证码   密码

4.1 视图类

class RegisterView(GenericViewSet, CreateModelMixin):
    queryset = User.objects.all()
    serializer_class = serializer.RegisterUserSerializer
    def create(self, request, *args, **kwargs):
        super().create(request, *args, **kwargs)
        return APIResponse(msg='注册成功')

4.2 序列化类

class RegisterUserSerializer(serializers.ModelSerializer):
    # code 不是User表的字段,必须重写
    code = serializers.CharField(max_length=4, min_length=4,write_only=True)

    class Meta:
        model = models.User
        fields = ['mobile', 'code', 'password']
        extra_kwargs = {'mobile': {'write_only': True},
                        'password': {'write_only': True}
                        }

    # 如果想让密码比较复杂,可以给密码写一个局部钩子,使用正则去匹配
    # 校验字段
    def validate(self, attrs):
        '''
        1 校验code是否正确
        2 把code从attrs中剔除
        3 使用手机号作为用户名,后期可以改
        '''
        code = attrs.get('code')
        mobile = attrs.get('mobile')
        # 校验code
        cache_code = cache.get(settings.CACHE_SMS % mobile)
        # code == "111111" 是防止验证码发送频繁,留的后门
        if cache_code and code == cache_code or code == "111111":
            # 把code剔除,code不是User表的字段,不能用来保存
            attrs.pop('code')
            # 创造一个username
            attrs['username'] = mobile
        else:
            raise ValidationError({'detail': '验证码错误'})
        return attrs

    # 为什么要重写create? 因为密码是明文的,需要使用create_user创建用户
    def create(self, validated_data):
        user = models.User.objects.create_user(**validated_data)
        return user

5 前端登录注册

# 多方式登录接口
# 校验手机号是否存在接口
# 发送验证码接口(腾讯短信平台)
# 验证码登录接口
# 验证码注册接口

5.1 Login.vue






5.2 Register.vue






5.3 Header.vue






相关