【15.0】登陆注册功能

【一】多方式登陆

【1】思路分析

(1)接口设计

  • 接口描述

    • 用户登录接口
  • 请求URL

    • /api/v1/user/userinfo/mul_login/
  • 请求方式

    • POST
  • Body请求参数(application/json)

参数名 必选 类型 说明
username string 用户名(支持用户名/邮箱/手机号)
password string 密码
  • 返回示例
    • 500认证成功
    • 401 认证失败
{
    "code": 100,
    "msg": "请求成功",
    "username": "admin",
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjkxNTY0MTE1LCJlbWFpbCI6IiJ9.RYxs3D6EjOfzLKGgF-QAav6k0sC_tCPWUd6tskGdlhI",
    "icon": "http://127.0.0.1:8000/media/icon/default.png"
}
  • 要求
    • 校验参数是否为空
    • 校验账号密码是否正确
    • 登陆之后返回token
    • 支持用户名多字段登陆,用户名可以使用手机号/邮箱/用户名登陆

【2】代码实现

  • 视图层
    • luffyCity\luffyCity\apps\user\views.py
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
from luffyCity.apps.user.models import User
from luffyCity.apps.user.serializers.mul_login_serializer import MulLoginSerializer
from luffyCity.utils.common_response import CommonResponse
from rest_framework.exceptions import APIException


# Create your views here.
class UserView(GenericViewSet):
    '''
    验证手机号接口
    get请求
    与数据库交互但不需要序列化
    继承 ViewSet 自动生成路由
    '''

    # 序列化类
    serializer_class = MulLoginSerializer

    # 校验手机号
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        '''
        get 请求 携带在地址参数
        :param request:
        :param args:
        :param kwargs:
        :return:
        '''
        try:
            mobile = request.query_params.get('mobile', None)
            User.objects.get(mobile=mobile)  # 有且只有一条才不会报错
            return CommonResponse(msg="手机号存在")
        except Exception as e:
            raise APIException("手机号不存在")

    # 多方式登陆 --- 序列化类校验数据
    @action(methods=['POST'], detail=False)
    def mul_login(self, request, *args, **kwargs):
        # 校验逻辑 --- 序列化类
        ser = self.get_serializer(data=request.data)
        # raise_exception:如果有错误,主动抛出异常,被全局异常捕获
        # is_valid : 触发字段的校验规则,局部钩子/全局钩子(全局钩子中写验证逻辑,签发token)
        ser.is_valid(raise_exception=True)
        username = ser.context.get('username')
        token = ser.context.get('token')
        icon = ser.context.get('icon')
        return CommonResponse(username=username, token=token, icon=icon)
  • 序列化类
    • luffyCity\luffyCity\apps\user\serializers\mul_login_serializer.py
# -*-coding: Utf-8 -*-
# @File : mul_login_serializer .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/9
from django.db.models import Q
from rest_framework import serializers
from ..models import User
from rest_framework.exceptions import APIException
from rest_framework_jwt.settings import api_settings
from django.conf import settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class MulLoginSerializer(serializers.ModelSerializer):
    # 字段自己的规则校验,一次性校验过不去,需要重写校验规则
    username = serializers.CharField(max_length=32, required=True)

    class Meta:
        model = User
        # 只做数据校验 ---- 写校验的字段
        fields = ['username', 'password']

    def _get_user(self, attrs):
        username = attrs.get('username')
        user = User.objects.filter(Q(username=username) | Q(mobile=username) | Q(email=username)).first()
        password = attrs.get('password')
        if user and user.check_password(password):
            # 用户存在且密码正确
            return user
        else:
            raise APIException("用户名或密码错误")

    def _get_token(self, user):
        # 签发token --- JWT
        payload = jwt_payload_handler(user)

        token = jwt_encode_handler(payload)

        return token

    def validate(self, attrs):
        # 验证用户密码逻辑 ---- 签发token
        # username 字段可能是 用户/手机号/邮箱 ---- 正则/Q查询
        user = self._get_user(attrs)
        # 校验成功 签发token
        token = self._get_token(user)
        # 用户名和token放在context(上下文管理器)中
        self.context['username'] = user.username
        self.context['icon'] = settings.BACKEND_URL + str(user.icon.url)
        self.context['token'] = token

        # attrs : 字典格式,校验通过的数据
        return attrs
  • luffyCity\luffyCity\utils\common_settings.py
BACKEND_URL = 'http://127.0.0.1:8000'
  • 路由
    • luffyCity\luffyCity\apps\user\urls.py
from django.urls import path
from .views import UserView
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('userinfo', UserView, 'userinfo')
urlpatterns = [

]
urlpatterns += router.urls

【二】验证码接口封装成包

【1】验证码发送主函数

  • luffyCity\luffyCity\libs\SMS_TencentCloud_Sender\SMS_Ten_Send.py
# -*-coding: Utf-8 -*-
# @File : SMS_Ten_Send .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/9
import os

# -*- coding: utf-8 -*-
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.sms.v20210111 import sms_client, models
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
import random
from . import settings


def get_verify_code(circle_bum):
    code = ''
    for i in range(circle_bum):
        code += str(random.randint(0, 9))
    return code


class SMS_TencentCloud_Sender():
    def __init__(self, code, phone):
        # 初始化 SecretId、SecretKey (为了安全防盗,置于本地环境变量中获取)
        self.cred = credential.Credential(settings.SECRET_ID, settings.SECRET_KEY)
        # 下发手机号码 : 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号
        self.phone = phone
        # 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,,若无模板参数,则设置为空
        # 验证码发送成功,验证码为:{self.code},{self.timeout}分钟内有效!
        # 目标验证码 , 目标超时时间(如果模板中设置了有效时间)
        self.template_param_set = [f'{code}', f'{settings.FAIL_TIME}']
        # APPID : 添加应用后生成的实际SdkAppId,示例如1400006666
        self.appid = settings.SMS_SDK_APPID
        # 必须填写已审核通过的签名
        self.sign_name = settings.SIGN_NAME
        # 必须填写已审核通过的模板 ID
        self.template_id = settings.TEMPLATE_ID

    def create_Client(self):
        httpProfile = HttpProfile()
        httpProfile.reqMethod = "POST"  # post请求(默认为post请求)
        httpProfile.reqTimeout = 30  # 请求超时时间,单位为秒(默认60秒)
        httpProfile.endpoint = "sms.tencentcloudapi.com"  # 指定接入地域域名(默认就近接入)

        # 实例化一个客户端配置对象,可以指定超时时间等配置
        clientProfile = ClientProfile()
        clientProfile.signMethod = "TC3-HMAC-SHA256"  # 指定签名算法
        clientProfile.language = "en-US"
        clientProfile.httpProfile = httpProfile

        # 实例化要请求产品(以sms为例)的client对象
        # 第二个参数是地域信息,可以直接填写字符串ap-guangzhou
        # 支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
        self.client = sms_client.SmsClient(self.cred, "ap-guangzhou", clientProfile)

    def create_Request(self):
        # 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
        req = models.SendSmsRequest()

        req.SmsSdkAppId = self.appid

        req.SignName = self.sign_name
        req.TemplateId = self.template_id
        req.TemplateParamSet = self.template_param_set
        req.PhoneNumberSet = [f"+86{self.phone}"]
        # 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回
        req.SessionContext = ""
        # 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手]
        req.ExtendCode = ""
        # 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。
        req.SenderId = ""

        resp = self.client.SendSms(req)

        # 输出json格式的字符串回包
        res_dict = resp._serialize(allow_none=True)
        if res_dict['SendStatusSet'][0]['Code'] == "Ok":
            return True
        else:
            print(res_dict)
            return False

    def send_sms(self):
        self.create_Client()
        return self.create_Request()


def tencent_sms_main(verify_code, tag_phone):
    try:
        t_sms = SMS_TencentCloud_Sender(verify_code, tag_phone)
        return t_sms.send_sms()
    except Exception as e:
        print(e)
        return False


if __name__ == '__main__':
    phone = ''
    code = get_verify_code(4)
    result = tencent_sms_main(code, phone)
    print(result)
    # print(os.environ)

【2】验证码发送配置文件

  • luffyCity\luffyCity\libs\SMS_TencentCloud_Sender\settings.py
# -*-coding: Utf-8 -*-
# @File : settings .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/9
import os

# 腾讯云账户密钥对 secretId
SECRET_ID = os.environ.get("TENCENTCLOUD_SECRET_ID")
# 腾讯云账户密钥对 secretKey
SECRET_KEY = os.environ.get("TENCENTCLOUD_SECRET_KEY")

# 添加应用后生成的实际SdkAppId
SMS_SDK_APPID = '1400837802'
# 必须填写已审核通过的签名
SIGN_NAME = '梦梦的知识星球公众号'
# 必须填写已审核通过的模板 ID
TEMPLATE_ID = '1891656'
# 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致
TEMPLATE_PARAM_SET = ''
# 用户的 session 内容(无需要可忽略)
SESSION_CONTEXT = ''
# 短信码号扩展号(无需要可忽略)
EXTEND_CODE = ''
# 国内短信无需填写该项
SENDER_ID = ''
# 验证码失效时间
FAIL_TIME = '2'

【3】验证码包的初始化文件

  • luffyCity\luffyCity\libs\SMS_TencentCloud_Sender\__init__.py
# -*-coding: Utf-8 -*-
# @File : __init__ .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/9

from .SMS_Ten_Send import get_verify_code, tencent_sms_main

【4】验证码发送接口的风险问题

验证码发送接口的风险问题主要是安全性和频率限制两个方面。

  • 首先,短信验证码的安全性是一个重要的问题。
    • 因为验证码通常用于身份验证或重要操作的确认,如果验证码泄露或被劫持,可能导致用户账号被盗或遭受其他安全威胁。

(1)提高安全性

数据加密:

  • 在验证码发送接口中,可以引入加密机制
  • 例如携带一个特定的加密串,使用对称或非对称加密算法将验证码数据进行加密,以防止数据被未授权人员获取并使用。

防止重放攻击:

  • 为了防止恶意用户重复使用已经获取到的验证码进行验证,可以在验证码数据中加入一些唯一标识符或时间戳,并在验证过程中验证其有效性。
  • 这样可以有效防止验证码的重放攻击。

(2)频率限制

  • 其次,频率限制也是必要的。通过限制发送验证码的频率,可以降低恶意用户滥用接口或对系统造成过大负载的风险。

IP限制:

  • 可以通过限制同一个IP地址在一段时间内发送验证码的次数,来限制恶意用户的行为。对于大流量代理服务器问题,可以考虑使用代理池技术,即在后台维护一个可用的代理IP池,通过轮询使用不同的IP地址发送验证码短信,以平均负载和降低单个IP的频率。

手机号限制:

  • 同一个手机号在一定时间内发送验证码的次数也需要进行限制,以防止恶意用户通过滥用接口对用户账号进行猜测或攻击。可以设置一个合理的时间间隔,在此时间间隔内只允许发送有限次数的验证码。

【三】短信验证登录

【1】主视图函数

from django.shortcuts import render, HttpResponse
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
from luffyCity.apps.user.models import User
from luffyCity.apps.user.serializers.mul_login_serializer import MulLoginSerializer, SmsLoginSerializer
from luffyCity.utils.common_response import CommonResponse
from rest_framework.exceptions import APIException
from luffyCity.libs.SMS_TencentCloud_Sender import get_verify_code, tencent_sms_main
from django.core.cache import cache


# Create your views here.
class UserView(GenericViewSet):
    '''
    验证手机号接口
    get请求
    与数据库交互但不需要序列化
    继承 ViewSet 自动生成路由
    '''

    # 序列化类
    serializer_class = MulLoginSerializer

    # 校验手机号
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        '''
        get 请求 携带在地址参数
        :param request:
        :param args:
        :param kwargs:
        :return:
        '''
        try:
            mobile = request.query_params.get('mobile', None)
            User.objects.get(mobile=mobile)  # 有且只有一条才不会报错
            return CommonResponse(msg="手机号存在")
        except Exception as e:
            raise APIException("手机号不存在")

    def _common_login(self, request, *args, **kwargs):
        # 校验逻辑 --- 序列化类
        ser = self.get_serializer(data=request.data)
        # raise_exception:如果有错误,主动抛出异常,被全局异常捕获
        # is_valid : 触发字段的校验规则,局部钩子/全局钩子(全局钩子中写验证逻辑,签发token)
        ser.is_valid(raise_exception=True)
        username = ser.context.get('username')
        token = ser.context.get('token')
        icon = ser.context.get('icon')
        return CommonResponse(username=username, token=token, icon=icon)

    # 多方式登陆 --- 序列化类校验数据
    @action(methods=['POST'], detail=False)
    def mul_login(self, request, *args, **kwargs):

        return self._common_login(request, *args, **kwargs)

    @action(methods=['GET'], detail=False)
    def send_sms(self, request, *args, **kwargs):

        # 前端把需要发送验证码的手机号传入,携带在地址栏中
        mobile = request.query_params.get('mobile', None)
        code = get_verify_code(4)  # 存储验证码,放到缓存内
        cache.set(f'sms_code_{mobile}', code)
        if mobile and tencent_sms_main(verify_code=code, tag_phone=mobile):
            return CommonResponse(msg="发送验证码成功")
        raise APIException("请输入手机号")

    def get_serializer_class(self):
        if self.action == 'sms_login':
            return SmsLoginSerializer
        else:
            return self.serializer_class

    @action(methods=['POST'], detail=False)
    def sms_login(self, request, *args, **kwargs):
        return self._common_login(request, *args, **kwargs)

【短信发送功能优化】异步处理

原来的发送短信,是同步

  • 前端输入手机号---》点击发送短信---》前端发送ajax请求----》到咱们后端接口---》取出手机号----》调用腾讯发送短信---》腾讯去发短信---》发完后----》回复给我们后端发送成功---》我们后端收到发送成功---》给我们前端返回发送成功

把腾讯发送短信的过程,变成异步

  • 前端输入手机号---》点击发送短信---》前端发送ajax请求----》到咱们后端接口---》取出手机号----》开启线程,去调用腾讯短信发送(异步)---》我们后端继续往后走----》直接返回给前端,告诉前端短信已发送
    -另一条发短信线程线程会去发送短信,至于是否成功,我们不管了
@action(methods=['GET'], detail=False)
def send_sms(self, request, *args, **kwargs):

    # 前端把需要发送验证码的手机号传入,携带在地址栏中
    mobile = request.query_params.get('mobile', None)
    code = get_verify_code(4)  # 存储验证码,放到缓存内
    cache.set(f'sms_code_{mobile}', code)
    if mobile:
        # 开启线程处理短信
        # tencent_sms_main(verify_code=code, tag_phone=mobile)
        t = Thread(target=tencent_sms_main, args=(code, mobile,))
        t.start()
        return CommonResponse(msg="验证码已发送")
    raise APIException("请输入手机号")

【2】序列化校验

# -*-coding: Utf-8 -*-
# @File : mul_login_serializer .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/9
from django.db.models import Q
from rest_framework import serializers
from ..models import User
from rest_framework.exceptions import APIException
from rest_framework_jwt.settings import api_settings
from django.conf import settings
from django.core.cache import cache

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class CommonLoginSerializer():
    def _get_user(self, attrs):
        username = attrs.get('username')
        user = User.objects.filter(Q(username=username) | Q(mobile=username) | Q(email=username)).first()
        password = attrs.get('password')
        if user and user.check_password(password):
            # 用户存在且密码正确
            return user
        else:
            raise APIException("用户名或密码错误")

    def _get_token(self, user):
        # 签发token --- JWT
        payload = jwt_payload_handler(user)

        token = jwt_encode_handler(payload)

        return token

    def validate(self, attrs):
        # 验证用户密码逻辑 ---- 签发token
        # username 字段可能是 用户/手机号/邮箱 ---- 正则/Q查询
        user = self._get_user(attrs)
        # 校验成功 签发token
        token = self._get_token(user)
        # 用户名和token放在context(上下文管理器)中
        self.context['username'] = user.username
        self.context['icon'] = settings.BACKEND_URL + str(user.icon.url)
        self.context['token'] = token

        # attrs : 字典格式,校验通过的数据
        return attrs


class MulLoginSerializer(CommonLoginSerializer, serializers.ModelSerializer):
    # 字段自己的规则校验,一次性校验过不去,需要重写校验规则
    username = serializers.CharField(max_length=32, required=True)

    class Meta:
        model = User
        # 只做数据校验 ---- 写校验的字段
        fields = ['username', 'password']

    def _get_user(self, attrs):
        username = attrs.get('username')
        user = User.objects.filter(Q(username=username) | Q(mobile=username) | Q(email=username)).first()
        password = attrs.get('password')
        if user and user.check_password(password):
            # 用户存在且密码正确
            return user
        else:
            raise APIException("用户名或密码错误")


class SmsLoginSerializer(CommonLoginSerializer, serializers.Serializer):
    code = serializers.CharField(max_length=4)
    mobile = serializers.CharField(max_length=11)

    def _get_user(self, attrs):
        # 从attrs中取出手机号和验证码
        # 验证验证码是否正确
        mobile = attrs.get('mobile')
        code = attrs.get('code')
        old_code = cache.get(f'sms_code_{mobile}')
        # 测试环节,高并发 DEBUG 开启并且给一个万能的验证码
        if code == old_code or (settings.DEBUG and code == '999'):
            # 验证码正确 , 查询用户
            user = User.objects.filter(mobile=mobile).first()
            if user:
                return user
            else:
                raise APIException("手机号不存在")
        else:
            raise APIException("验证码错误")

【四】注册功能

【2】功能实现

  • 视图函数
    • luffyCity\luffyCity\apps\user\views.py
from threading import Thread

from django.shortcuts import render, HttpResponse
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin
from rest_framework.decorators import action
from luffyCity.apps.user.models import User
from luffyCity.apps.user.serializers.mul_login_serializer import MulLoginSerializer, SmsLoginSerializer, \
    UserRegisterSerializer
from luffyCity.utils.common_response import CommonResponse
from rest_framework.exceptions import APIException
from luffyCity.libs.SMS_TencentCloud_Sender import get_verify_code, tencent_sms_main
from django.core.cache import cache


# Create your views here.
class UserView(GenericViewSet, CreateModelMixin):
    '''
    验证手机号接口
    get请求
    与数据库交互但不需要序列化
    继承 ViewSet 自动生成路由
    '''

    # 序列化类
    serializer_class = MulLoginSerializer

    # 校验手机号
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        '''
        get 请求 携带在地址参数
        :param request:
        :param args:
        :param kwargs:
        :return:
        '''
        try:
            mobile = request.query_params.get('mobile', None)
            User.objects.get(mobile=mobile)  # 有且只有一条才不会报错
            return CommonResponse(msg="手机号存在")
        except Exception as e:
            raise APIException("手机号不存在")

    def _common_login(self, request, *args, **kwargs):
        # 校验逻辑 --- 序列化类
        ser = self.get_serializer(data=request.data)
        # raise_exception:如果有错误,主动抛出异常,被全局异常捕获
        # is_valid : 触发字段的校验规则,局部钩子/全局钩子(全局钩子中写验证逻辑,签发token)
        ser.is_valid(raise_exception=True)
        username = ser.context.get('username')
        token = ser.context.get('token')
        icon = ser.context.get('icon')
        return CommonResponse(username=username, token=token, icon=icon)

    # 多方式登陆 --- 序列化类校验数据
    @action(methods=['POST'], detail=False)
    def mul_login(self, request, *args, **kwargs):

        return self._common_login(request, *args, **kwargs)

    # @action(methods=['GET'], detail=False)
    # def send_sms(self, request, *args, **kwargs):
    #
    #     # 前端把需要发送验证码的手机号传入,携带在地址栏中
    #     mobile = request.query_params.get('mobile', None)
    #     code = get_verify_code(4)  # 存储验证码,放到缓存内
    #     cache.set(f'sms_code_{mobile}', code)
    #     if mobile and tencent_sms_main(verify_code=code, tag_phone=mobile):
    #         return CommonResponse(msg="发送验证码成功")
    #     raise APIException("请输入手机号")

    @action(methods=['GET'], detail=False)
    def send_sms(self, request, *args, **kwargs):

        # 前端把需要发送验证码的手机号传入,携带在地址栏中
        mobile = request.query_params.get('mobile', None)
        code = get_verify_code(4)  # 存储验证码,放到缓存内
        cache.set(f'sms_code_{mobile}', code)
        if mobile:
            # 开启线程处理短信
            # tencent_sms_main(verify_code=code, tag_phone=mobile)
            t = Thread(target=tencent_sms_main, args=(code, mobile,))
            t.start()
            return CommonResponse(msg="验证码已发送")
        raise APIException("请输入手机号")

    def get_serializer_class(self):
        if self.action == 'sms_login':
            return SmsLoginSerializer
        elif self.action == 'register' or self.action == 'create':
            return UserRegisterSerializer
        else:
            return self.serializer_class

    @action(methods=['POST'], detail=False)
    def sms_login(self, request, *args, **kwargs):
        return self._common_login(request, *args, **kwargs)

    # 自己写的  访问:127.0.0.1:8000/api/v1/user/userinfo/register/   --->post请求即可
    @action(methods=['POST'], detail=False)
    def register(self, request, *args, **kwargs):
        ser = self.get_serializer(data=request.data)
        ser.is_valid(raise_exception=True)
        ser.save()
        # super().create(request, *args, **kwargs)  # 只要这样写,又会走序列化
        return CommonResponse(msg='注册成功')

    # 不自己写了,只要继承CreateModelMixin,访问:127.0.0.1:8000/api/v1/user/userinfo   --->post请求即可
    # 这个我们不用写,它有  只要post请求过来,就会执行create
    # def create(self, request, *args, **kwargs):
    #     serializer = self.get_serializer(data=request.data) # 第一个错误  UserRegisterSerializer
    #     serializer.is_valid(raise_exception=True) #执行三个校验:字段自己,局部钩子,全局
    #     self.perform_create(serializer)
    #     # 序列化要调用它,只要调用serializer.data ,就会走序列化,只要走序列化,会把create返回的user对象 来使用UserRegisterSerializer类做序列化
    #     return CommonResponse(msg='注册成功') #不走序列化了,序列类中得write_only 也就不用了
  • 序列化类
    • luffyCity\luffyCity\apps\user\serializers\mul_login_serializer.py

# 注册:1 校验数据  2 保存  3 序列化用不要?存疑
class UserRegisterSerializer(serializers.ModelSerializer):
    code = serializers.CharField(max_length=4, min_length=4, write_only=True)

    class Meta:
        model = User
        fields = ['mobile', 'password', 'code']  # code 不是数据库的字段,需要重写

    # 如果要限制密码强度,需要写个局部钩子
    def _check_code(self, attrs):
        mobile = attrs.get('mobile')
        code = attrs.get('code')
        old_code = cache.get('send_sms_code_%s' % mobile)
        if not (code == old_code or (settings.DEBUG and code == '9999')):  # 第二个错误:debug忘了设为True
            raise APIException("验证码错误")

    def _pre_save(self, attrs):  # {mobile:122222,code:8888,password:123}
        attrs.pop('code')
        attrs['username'] = attrs.get('mobile')  # 默认用户名就是手机号  可以随机生成用户名  随机生成有意义的名字( Faker)

    def validate(self, attrs):
        # 写逻辑
        # 1 校验验证码是否正确
        self._check_code(attrs)
        # 2 入口前准备 ---》code不是数据库字段,不能入库,username是数据库字段必填,这里没有,写成默认
        self._pre_save(attrs)
        return attrs

    def create(self, validated_data):  # {mobile:122222,password:123,username:名字}
        # 为什么要重写create? 因为密码人家是加密的,如果不写用的是
        # User.objects.create(**validated_data)  # 密码是铭文,必须重写
        user = User.objects.create_user(**validated_data)  # 保存成功,密码就是加密的
        return user

posted @ 2023-08-19 17:09  Chimengmeng  阅读(15)  评论(0编辑  收藏  举报
/* */