• drf——基于jwt的多方式登录以及自定义多方式登录


    一、基于jwt的多方式登陆

    1 手机号+密码   用户名+密码  邮箱+密码
    2 流程分析(post请求):
        -路由:自动生成(推荐自动生成,自己手写也行)  
        -视图类:ViewSet(ViewSetMixin, views.APIView)
        -序列化类:重写validate方法,在这里面对用户名和密码进行校验

    代码实现

    models.py----->进行数据迁移

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    
    
    class UserInfo(AbstractUser):
        phone = models.CharField(max_length=32, unique=True)
    

    settings.py

    INSTALLED_APPS = [
        ...
        'rest_framework'
    ]
    #扩写AUTH_USER表
    AUTH_USER_MODEL = 'app01.UserInfo'
    REST_FRAMEWORK = {
        # 配置全局异常
        'EXCEPTION_HANDLER': 'app01.utils.common_exception'
    }

    views.py

    from rest_framework.viewsets import ViewSet
    from app01.serializer import LoginSerializer
    from app01.utils import APIResponse
    
    
    class LoginViewSet(ViewSet):
        def create(self, request, *args, **kwargs):
            # 实例化得到一个序列化类的对象
            # ser=LoginSerializer(data=request.data,context={'request':request})
            ser = LoginSerializer(data=request.data)
            # 序列化类的对象的校验方法
            ser.is_valid(raise_exception=True)  # 字段自己的校验,局部钩子校验,全局钩子校验
            # 如果通过,表示登录成功,返回手动签发的token
            token = ser.context.get('token')
            username = ser.context.get('username')
            return APIResponse(token=token, username=username)
            # 如果失败,不用管了

    serializer.py

    from rest_framework import serializers
    from app01.models import UserInfo
    
    import re
    from rest_framework.exceptions import ValidationError
    from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
    
    
    class LoginSerializer(serializers.ModelSerializer):
        #重写username不然报错
        username = serializers.CharField()
    
        class Meta:
            model = UserInfo
            fields = ['username', 'password']
    
        def validate(self, attrs):
            # username可能是邮箱,手机号,用户名
            username = attrs.get('username')
            password = attrs.get('password')
            # 如果是手机号
            if re.match('^1[3-9]d{9}$', username):
                # 以手机号登录
                user = UserInfo.objects.filter(phone=username).first()
            elif re.match('^.+@.+$', username):
                # 以邮箱登录
                user = UserInfo.objects.filter(email=username).first()
            else:
                # 以用户名登录
                user = UserInfo.objects.filter(username=username).first()
            # 如果user有值并且密码正确
            if user and user.check_password(password):
                # 登录成功,生成token
                # drf-jwt中有通过user对象生成token的方法
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                # token是要在视图类中使用,现在我们在序列化类中
                # self.context.get('request')
                # 视图类和序列化类之间通过context这个字典来传递数据
                self.context['token'] = token
                self.context['username'] = user.username
                #一定要记得return
                return attrs
    
            else:
                raise ValidationError('用户名或密码错误')

    utils.py

    from rest_framework.response import Response
    
    
    class APIResponse(Response):
        def __init__(self, code=100, msg='成功', data=None, status=None,
                     headers=None, content_type=None, **kwargs):
            dic = {'code': code, 'msg': msg}
            if data:
                dic['data'] = data
            dic.update(kwargs)
            super().__init__(data=dic, status=status, headers=headers, content_type=content_type)
    
    
    from rest_framework.views import exception_handler
    
    #全局异常捕获
    def common_exception(exc, context):
        # 先调用REST framework默认的异常处理方法获得标准错误响应对象
        response = exception_handler(exc, context)
        # 在此处补充自定义的异常处理
        if response is None:
            response = Response(data={'code':999,'msg':str(exc)})
    
        return response

    urls.py

    注意:自动生成路由,四种对应关系

    from django.urls import path
    from rest_framework.routers import SimpleRouter
    from app01 import views
    
    router = SimpleRouter()
    #必须要加,basename='login',不然会报错
    router.register('login', views.LoginViewSet,basename='login')
    print(router.urls)
    urlpatterns = [
        ...
      #path('login/', views.LoginViewSet.as_view({'post':'create'})), 可以用这种自己手写的路由 ] urlpatterns
    += router.urls

    登录方式:在http://127.0.0.1:8000/login/发送post请求,携带json格式username,password

    二、自定义user表,签发token,认证类的代码实现多方式登录

    models.py

    from django.db import models
    
    class MyUser(models.Model):
        username = models.CharField(max_length=32) #字段名一定要叫username不然要自己重写,具体看源码
        password = models.CharField(max_length=32)
        phone = models.CharField(max_length=32)
        email = models.EmailField()

    utils.py

    from rest_framework.response import Response
    
    
    class APIResponse(Response):
        def __init__(self, code=100, msg='成功', data=None, status=None,
                     headers=None, content_type=None, **kwargs):
            dic = {'code': code, 'msg': msg}
            if data:
                dic['data'] = data
            dic.update(kwargs)
            super().__init__(data=dic, status=status, headers=headers, content_type=content_type)
    
    
    from rest_framework.views import exception_handler
    
    #全局异常捕获
    def common_exception(exc, context):
        # 先调用REST framework默认的异常处理方法获得标准错误响应对象
        response = exception_handler(exc, context)
        # 在此处补充自定义的异常处理
        if response is None:
            response = Response(data={'code':999,'msg':str(exc)})
    
        return response

    views.py

    from rest_framework.views import APIView
    from app01.utils import APIResponse
    import re
    from app01.models import MyUser
    from rest_framework_jwt.settings import api_settings
    
    jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
    jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    from rest_framework_jwt.views import obtain_jwt_token
    
    
    class MyLoginView(APIView):
        def post(self, request, *args, **kwargs):
            username = request.data.get('username')
            password = request.data.get('password')
            # 如果是手机号
            if re.match('^1[3-9]d{9}$', username):
                # 以手机号登录
                user = MyUser.objects.filter(phone=username).first()
            elif re.match('^.+@.+$', username):
                # 以邮箱登录
                user = MyUser.objects.filter(email=username).first()
            else:
                # 以用户名登录
                user = MyUser.objects.filter(username=username).first()
            # 如果user有值并且密码正确,注意这里user.password == password
            if user and user.password == password:
                # 登录成功,生成token
                # drf-jwt中有通过user对象生成token的方法
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                return APIResponse(token=token, username=user.username)
            else:
                return APIResponse(code=101, msg='用户名或密码错误')

    urls.py

    from django.contrib import admin
    from django.urls import path
    from app01 import views
    
    urlpatterns = [
        path('login2/', views.MyLoginView.as_view()),
    ]

    settings.py

    INSTALLED_APPS = [
        ...
        'rest_framework'
    ]
    REST_FRAMEWORK = {
        # 配置全局异常
        'EXCEPTION_HANDLER': 'app01.utils.common_exception'
    }

    在自定义登录的基础上,加上自定义的认证,来查询订单信息

    auth.py

    from  rest_framework_jwt.utils import jwt_decode_handler
    import jwt
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
    
    from app01.models import MyUser
    class JwtAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            token=request.META.get('HTTP_Authorization'.upper())
            try:
                payload = jwt_decode_handler(token)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('过期了')
            except jwt.DecodeError:
                raise AuthenticationFailed('解码错误')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('不合法的token')
            # 得到的user对象,应该是自己user表的user对象
            print(payload)
            # user=MyUser.objects.get(id=payload['user_id']) 这样写不好,会每次都查一次数据库
            user=payload #不用每次查数据库
         #或者user = MyUser(id=payload["user_id"], username = payload["username"])不用每次查数据库
         return (user, token)

    views.py 加上以下认证代码

    from app01.auth import JwtAuthentication
    class OrderAPIView(APIView):
        authentication_classes = [JwtAuthentication, ]
    
        def get(self, request):
            # print(request.user) # 自己的user对象
            print(request.user)  # user是个字典,内部有user_id,
            # 后续要查询该用户的所有订单,直接根据user_id查询即可
            return APIResponse(msg='查询订单成功')

    urls.py

    urlpatterns = [
        path('order/', views.OrderAPIView.as_view()),
    ]
  • 相关阅读:
    CSPS模拟 65
    CSPS模拟 64
    $color$有色图
    CSPS模拟 63
    CSPS模拟 62
    CSPS模拟 61
    CSPS模拟 60
    CSPS模拟 59
    go中单链表
    MYSQL-联合索引
  • 原文地址:https://www.cnblogs.com/guojieying/p/13966275.html
Copyright © 2020-2023  润新知