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

1、基于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.pydjango

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

views.pyjson

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.pyapi

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.pyapp

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.pypost

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

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,passwordspa

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

models.pyrest

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.pycode

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()),
]