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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM