zoukankan      html  css  js  c++  java
  • 路飞-注册登录后台

    后台

    后台路由:user/urls.py

    from django.urls import path
    from . import views
    urlpatterns = [
        path('mobile/', views.MobileAPIView.as_view()),
        path('sms/', views.SMSAPIView.as_view()),
        path('register/', views.RegisterCreateAPIView.as_view()),
        path('login/', views.LoginAPIView.as_view()),
        path('login/mobile/', views.LoginMobileAPIView.as_view()),
    ]
    

    常量配置:settings/const.py | settings/dev.py

    # settings/const.py
    
    # 短信过期时间(单位:s)
    SMS_EXP = 300000
    # 短信缓存key
    SMS_CACHE_KEY = 'sms_%(mobile)s'
    # 轮播图推荐量
    BANNER_COUNT = 3
    
    
    # settings/dev.py
    REST_FRAMEWORK = {
        # 异常配置
        'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
        # 频率限制配置
        'DEFAULT_THROTTLE_RATES': {
            'user': None,
            'anon': None,
            'sms': '1/m',
        },
    }
    

    序列化组件:user/serializers.py

    from rest_framework import serializers
    from . import models
    import re
    from django.core.cache import cache
    from settings.const import SMS_CACHE_KEY
    class RegisterModelSerializer(serializers.ModelSerializer):
        # 自定义反序列化字段的规则必须在字段声明时规定
        code = serializers.CharField(write_only=True, min_length=4, max_length=4)
        class Meta:
            model = models.User
            fields = ('mobile', 'password', 'code', 'username', 'email')
            extra_kwargs = {
                'password': {
                    'min_length': 6,
                    'max_length': 18,
                    'write_only': True
                },
                'username': {
                    'read_only': True
                },
                'email': {
                    'read_only': True
                }
            }
    
        def validate_mobile(self, value):
            if not re.match(r'^1[3-9]d{9}$', value):
                raise serializers.ValidationError('手机号有误')
            return value
    
        def validate(self, attrs):
            mobile = attrs.get('mobile')
            code = attrs.pop('code')  # code不入库
            old_code = cache.get(SMS_CACHE_KEY % {'mobile': mobile})
            if not old_code:
                raise serializers.ValidationError({'code': '验证码已失效'})
            if code != old_code:
                raise serializers.ValidationError({'code': '验证码错误'})
            # 验证码一旦验证成功,就失效(一次性)
            # cache.set(SMS_CACHE_KEY % {'mobile': mobile}, '0000', 1)
            return attrs
    
        # create方法重写:通过手机号注册的用户,用户名默认就是手机号
        def create(self, validated_data):
            mobile = validated_data.get('mobile')
            username = mobile
            password = validated_data.get('password')
            return models.User.objects.create_user(username=username, mobile=mobile, password=password)
    
    
    
    from rest_framework_jwt.serializers import jwt_payload_handler
    from rest_framework_jwt.serializers import jwt_encode_handler
    class LoginModelSerializer(serializers.ModelSerializer):
        usr = serializers.CharField(write_only=True)
        pwd = serializers.CharField(write_only=True)
        class Meta:
            model = models.User
            fields = ['usr', 'pwd', 'username', 'mobile', 'email']
            extra_kwargs = {
                'username': {
                    'read_only': True
                },
                'mobile': {
                    'read_only': True
                },
                'email': {
                    'read_only': True
                },
            }
    
        def validate(self, attrs):
            usr = attrs.get('usr')
            pwd = attrs.get('pwd')
    
            # 多方式登录:各分支处理得到该方式下对应的用户
            if re.match(r'.+@.+', usr):
                user_query = models.User.objects.filter(email=usr)
            elif re.match(r'1[3-9][0-9]{9}', usr):
                user_query = models.User.objects.filter(mobile=usr)
            else:
                user_query = models.User.objects.filter(username=usr)
            user_obj = user_query.first()
    
            # 签发:得到登录用户,签发token并存储在实例化对象中
            if user_obj and user_obj.check_password(pwd):
                # 签发token,将token存放到 实例化类对象的token 名字中
                payload = jwt_payload_handler(user_obj)
                token = jwt_encode_handler(payload)
                # 将当前用户与签发的token都保存在序列化对象中
                self.user = user_obj
                self.token = token
                return attrs
    
            raise serializers.ValidationError({'data': '数据有误'})
    

    频率组件:user/thorttles.py

    from rest_framework.throttling import SimpleRateThrottle
    
    class SMSRateThrottle(SimpleRateThrottle):
        scope = 'sms'
        def get_cache_key(self, request, view):
            mobile = request.data.get('mobile') or request.query_params.get('mobile')
            if not mobile:
                return None
            return self.cache_format % {'scope': self.scope, 'ident': mobile}
    

    异常模块:utils/exception.py

    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework import status
    from utils.logging import logger
    from utils.response import APIResponse
    def exception_handler(exc, context):
        response = drf_exception_handler(exc, context)
        if response is None:
            logger.error('%s - %s - %s' % (context['view'], context['request'].method, exc))
            return APIResponse(3, '异常',
                results={'detail': '服务器错误'},
               http_status=status.HTTP_500_INTERNAL_SERVER_ERROR,
               exception=True
            )
        return APIResponse(3, '异常', results=response.data, http_status=status.HTTP_401_UNAUTHORIZED)
    

    视图模块:user/views.py

    from rest_framework.views import APIView
    from .models import User
    from utils.response import APIResponse
    import re
    # 注册逻辑:1.校验手机号是否存在 2.发送验证码 3.完成注册
    # 校验手机号
    class MobileAPIView(APIView):
        def post(self, request, *args, **kwargs):
            mobile = request.data.get('mobile')
            if not mobile or not re.match(r'^1[3-9]d{9}$', mobile):
                return APIResponse(1, '数据有误')
            try:
                User.objects.get(mobile=mobile)
                return APIResponse(2, '已注册')
            except:
                return APIResponse(0, '未注册')
    
    
    # 发送验证码
    from libs import txsms
    from django.core.cache import cache
    from settings.const import SMS_EXP, SMS_CACHE_KEY
    from .thorttles import SMSRateThrottle
    class SMSAPIView(APIView):
        # 频率限制
        throttle_classes = [SMSRateThrottle]
        def post(self, request, *args, **kwargs):
            # 1)拿到前台的手机号
            mobile = request.data.get('mobile')
            if not mobile or not re.match(r'^1[3-9]d{9}$', mobile):
                return APIResponse(2, '数据有误')
            # 2)调用txsms生成手机验证码
            code = txsms.get_code()
            # 3)调用txsms发送手机验证码
            result = txsms.send_sms(mobile, code, SMS_EXP // 60)
            # 4)失败反馈信息给前台
            if not result:
                return APIResponse(1, '短信发送失败')
            # 5)成功服务器缓存手机验证码 - 用缓存存储(方便管理) - redis
            cache.set(SMS_CACHE_KEY % {'mobile': mobile}, code, SMS_EXP)
            # 6)反馈成功信息给前台
            return APIResponse(0, '短信发送成功')
    
    # 注册
    from rest_framework.generics import CreateAPIView
    from . import serializers
    class RegisterCreateAPIView(CreateAPIView):
        # queryset = User.objects.filter(is_active=True)
        serializer_class = serializers.RegisterModelSerializer
    
        # 自定义响应结果
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)  # 校验失败就主动抛异常 => 自定义异常结果,配置异常模块
            user_obj = serializer.save()  # 要自定义入库逻辑,重写create方法
            headers = self.get_success_headers(serializer.data)
            # 响应结果需要格式化,使用序列化类要提供序列化与反序列化两套规则
            return APIResponse(0, 'ok',
                results=serializers.RegisterModelSerializer(user_obj).data,
                http_status=201,
                headers=headers
                )
    
    
    # 多方式登录
    class LoginAPIView(APIView):
        # 1) 禁用认证与权限组件
        authentication_classes = []
        permission_classes = []
        def post(self, request, *args, **kwargs):
            # 2) 拿到前台登录信息,交给序列化类,规则:账号用usr传,密码用pwd传
            user_ser = serializers.LoginModelSerializer(data=request.data)
            # 3) 序列化类校验得到登录用户与token存放在序列化对象中
            user_ser.is_valid(raise_exception=True)
            # 4) 取出登录用户与token返回给前台
            return APIResponse(token=user_ser.token, results=serializers.LoginModelSerializer(user_ser.user).data)
    
    
    # 手机验证码登录
    from rest_framework_jwt.serializers import jwt_payload_handler
    from rest_framework_jwt.serializers import jwt_encode_handler
    class LoginMobileAPIView(APIView):
        authentication_classes = []
        permission_classes = []
        def post(self, request, *args, **kwargs):
            mobile = request.data.get('mobile')
            code = request.data.get('code')
            if not mobile or not code:
                return APIResponse(1, '数据有误')
            old_code = cache.get(SMS_CACHE_KEY % {'mobile': mobile})
            if code != old_code:
                return APIResponse(1, '验证码错误')
            try:
                user = User.objects.get(mobile=mobile)
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                return APIResponse(token=token, results=serializers.LoginModelSerializer(user).data)
            except:
                return APIResponse(1, '用户不存在')
    
  • 相关阅读:
    vue递归组件的实现
    Vue左滑组件slider的实现
    vue 全局引用jq(打包后可能会遇到的问题)
    vue simple框架打包遇到报错问题
    HTML5 FormData实现文件上传实例
    长连接、短连接、长轮询和WebSocket
    解决axios IE11 Promise对象未定义
    Html5的map在实际使用中遇到的问题及解决方案
    Js参数RSA加密传输,jsencrypt.js的使用
    jQuery火箭图标返回顶部代码
  • 原文地址:https://www.cnblogs.com/DcentMan/p/11844675.html
Copyright © 2011-2022 走看看