0011 注册登录(第四步:创建用户)

1 在Applications/Organization/views/Register.py文件中,增加注册序列化器和注册视图

  至此Register.py文件全部完成,完整代码如下:

from rest_framework.views import APIView
import logging
from itsdangerous import TimedJSONWebSignatureSerializer as TJWSSerializer
from django.conf import settings
from rest_framework.response import Response
from rest_framework import status

from coreapi import Field
from coreschema import String
from GeneralTools.CustomSchema import CustomSchema
from GeneralTools import Constants
from GeneralTools import Verifications

from rest_framework.generics import CreateAPIView
from rest_framework import serializers
import re
from rest_framework_jwt.settings import api_settings
from itsdangerous import BadData
from django_redis import get_redis_connection
from GeneralTools import SendSMSCode
from Applications.Organization.models import UserInfo

# 获取在配置文件中定义的logger,用来记录日志
logger = logging.getLogger('tongheng2')


class RegisterSerializer(serializers.ModelSerializer):
    """
    用户注册序列化器
    """

    # write_only 表明该字段仅用于反序列化输入,默认False
    sms_code = serializers.CharField(label='短信验证码', write_only=True, help_text='短信验证码', min_length=6, max_length=6)
    # read_only 表明该字段仅用于序列化输出,默认False
    token = serializers.CharField(label='JWT Token', read_only=True, help_text='JWT Token')

    class Meta:
        model = UserInfo
        fields = ('id', 'password', 'mobile', 'sms_code', 'token', 'openid')
        extra_kwargs = {
            'id': {
                'read_only': True,
                'help_text': '用户ID'
            },
            'password': {
                'write_only': True,
                'min_length': 6,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许6-20个字符的密码',
                    'max_length': '仅允许6-20个字符的密码',
                },
                'help_text': '密码'
            },
            'mobile': {
                'min_length': 11,
                'max_length': 11,
                'error_messages': {
                    'min_length': '请输入11位有效手机号码',
                    'max_length': '请输入11位有效手机号码',
                },
                'help_text': '手机号'
            },
            'openid': {
                'help_text': '微信openID'
            },
        }

    def validate_mobile(self, value):
        """
        验证手机号
        """

        if not re.match(r'^1[3-9]\d{9}$', value):
            raise serializers.ValidationError('手机号格式错误')

        # 手机号是否重复
        # count = UserInfo.objects.filter(mobile=value).count()
        # if count > 0:
        #     raise serializers.ValidationError('手机号已存在')

        return value

    def validate_openid(self, value):
        """
        验证微信openID
        """

        # openID是否重复
        # count = UserInfo.objects.filter(openid=value).count()
        # if count > 0:
        #     raise serializers.ValidationError('openID已存在')

        return value

    def validate(self, attrs):
        """
        验证短信验证码
        """

        mobile = attrs['mobile']
        sms_code = attrs['sms_code']
        # logger.info("sms_code:%s" % sms_code)

        # 从Redis取出真实短信验证码
        conn = get_redis_connection("sms_codes")
        real_sms_code = conn.get("sms_%s" % mobile)
        # logger.info("real_sms_code:%s" % real_sms_code)

        if real_sms_code is None:
            # 过期或者不存在
            raise serializers.ValidationError('短信验证码已过期')

        # 验证短信验证码
        real_sms_code = real_sms_code.decode()
        if real_sms_code != sms_code:
            raise serializers.ValidationError('短信验证码错误')

        return attrs

    def create(self, validated_data):
        """
        更新到数据库
        """
        # print(self.context['request'].get('mobile'))
        # 移除数据库模型类中不存在的属性
        del validated_data['sms_code']
        validated_data['username'] = validated_data['mobile']
        # openid长度为1表示在电脑上注册,否则表示在微信上注册
        user = UserInfo.objects.filter(mobile=validated_data['mobile']).first()
        if len(validated_data['openid']) != '1':  # 如果在微信上注册,需要检查该手机是否在电脑上注册过,如果注册了,则直接更新
            if user is None:
                user = super().create(validated_data)
            else:
                user.openid = validated_data['openid']
                user.save()
        else:
            if user is None:
                user = super().create(validated_data)

        # 调用django的认证系统加密密码,由前端任意传6位数字
        # user.set_password(validated_data['password'])
        # user.save()

        # 注册成功就让用户处于登录状态
        # 由服务器签发一个jwt token,保存用户身份信息
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

        # 生成载荷信息(payload)
        payload = jwt_payload_handler(user)
        # 生成jwt token
        token = jwt_encode_handler(payload)

        # 给user对象增加一个属性token,保存jwt token信息
        user.token = token

        return user


class GetRegisterToken(APIView):
    """
    用户注册第一步:获取短信接口access token
    """

    schema = CustomSchema(
        manual_fields={
            'get': [
                Field(name="mobile", required=True, location="query", schema=String(description='手机号')),
            ],
        }
    )

    @classmethod
    def get(cls, request):
        """
        【功能描述】根据上传的手机号生成短信接口access token </br>
        【返回值】返回短信接口access token </br>
        """

        mobile = request.query_params.get('mobile')
        if not mobile:
            return Response(data={'message': '缺少mobile参数'}, status=status.HTTP_400_BAD_REQUEST)

        if not Verifications.mobileVerify(mobile):
            return Response(data={'message': '不是有效的有机号'}, status=status.HTTP_400_BAD_REQUEST)
        # 创建itsdangerous模型的转换工具
        tjwserializer = TJWSSerializer(settings.SECRET_KEY, Constants.VERIFY_ACCESS_TOKEN_EXPIRES)
        access_token = tjwserializer.dumps({'mobile': mobile})  # bytes
        access_token = access_token.decode()  # str

        data = {
            'mobile': mobile,
            'access_token': access_token
        }
        return Response(data=data, status=status.HTTP_200_OK)


class SendSMS(APIView):
    """
    用户注册第二步:发送短信验证码
    """

    schema = CustomSchema(
        manual_fields={
            'get': [
                Field(name="access_token", required=True, location="query", schema=String(description='access token')),
            ],
        }
    )

    @classmethod
    def get(cls, request):
        """
        【功能描述】根据上传的access_token,发送手机验证码 </br>
        【上传格式】/Organization/SendSMS/?access_token=*** </br>
        【返回值】发送成功返回200</br>
        """

        access_token = request.query_params.get('access_token')
        if not access_token:
            return Response(data={'message': '缺少access_token参数'}, status=status.HTTP_400_BAD_REQUEST)

        # 校验access_token
        tjwserializer = TJWSSerializer(settings.SECRET_KEY, Constants.VERIFY_ACCESS_TOKEN_EXPIRES)

        try:
            data = tjwserializer.loads(access_token)
            mobile = data['mobile']
            # 60秒之内只允许发送一次验证码
            # send_flag_<mobile> = 1,由Redis维护60秒有效期
            conn = get_redis_connection("sms_codes")
            send_flag = conn.get("send_flag_%s" % mobile)
            if send_flag:
                return Response(data={'message': '发送短信次数过于频繁'}, status=status.HTTP_400_BAD_REQUEST)
            # 发送短信验证码
            param = '手机注册'
            code = SendSMSCode.send_sms_code(mobile, param)
            if code == 0:
                data = {'message': 'ok'}
            else:
                return Response(data={'message': '发送短信验失败'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        except BadData as e:
            logger.error(e)
            return Response(data={'message': '无效access_token参数'}, status=status.HTTP_400_BAD_REQUEST)

        return Response(data=data, status=status.HTTP_200_OK)


#
#
class CreateUser(CreateAPIView):
    """
    【功能描述】 用户注册第三步:创建用户</br>
    【上传格式】</br>
            
    1 密码:随意上传六位数字或字母即可</br>
            
    2 openid:如果openid为空,则上传'0',否则上传openid</br>
    """
    serializer_class = RegisterSerializer

2 增加Organization url

from django.urls import path
from .views import Register, LoginHome, FindPassword

urlpatterns = [
    path('LoginHome/', LoginHome.LoginHome.as_view()),
    path('FindPassword/', FindPassword.FindPassword.as_view()),
    path('GetRegisterToken/', Register.GetRegisterToken.as_view()),
    path('SendSMS/', Register.SendSMS.as_view()),
    path('CreateUser/', Register.CreateUser.as_view()),
]

3 运行工程,访问接口文档

猜你喜欢

转载自www.cnblogs.com/dorian/p/12355224.html