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()),
]
相關文章
相關標籤/搜索