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 运行工程,访问接口文档