DRF認證流程及源碼分析

認證

前言

用戶驗證用戶是否合法登錄。
部份內容在DRF視圖的使用及源碼流程分析講解,建議先看講解視圖的這篇文章。html

使用流程

認證使用的方法流程以下:python

  1. 自定義認證類,繼承BaseAuthentication,而且覆寫其authenticate方法。不繼承BaseAuthentication也能夠,但認證類中必須聲明authenticateauthenticate_header兩個方法。
  2. 當認證經過後應該返回兩個值(一個元組),而且第一個值會傳遞給request.user這個屬性中,第二個值將會傳遞給request.auth這個屬性中。
  3. 若是認證失敗,則拋出異常APIException或者AuthenticationFailed,它會自動捕獲並返回。
  4. 當前認證類設置是全局使用仍是局部使用。

自定義認證類:app

完成一、二、3步,異常統一返回AuthenticationFailed函數

import jwt
from jwt import exceptions
import rest_framework.exceptions as rest_exception
from rest_framework.authentication import BaseAuthentication
from rest_framework import status
from app1.models import Login
from libs.TokenManager import SALT


class MyAuthentication(BaseAuthentication):

    def authenticate(self, request):
        token = request.META.get("HTTP_TOKEN")  # 在請求頭中設置token值,獲取的是HTTP_TOKEN
        if not token:
            raise rest_exception.AuthenticationFailed(code=status.HTTP_401_UNAUTHORIZED, detail="請在請求頭中設置token值!")
        try:    # 解析token
            verified_payload = jwt.decode(token, SALT, "HS256")
            user_id, username = verified_payload["user_id"], verified_payload["username"]
            user = Login.objects.filter(user=user_id, token=token).first()
            if user:
                return user_id, username
            else:
                raise rest_exception.AuthenticationFailed(code=status.HTTP_401_UNAUTHORIZED, detail="用戶不存在!")
        except exceptions.ExpiredSignatureError:
            raise rest_exception.AuthenticationFailed(code=status.HTTP_401_UNAUTHORIZED, detail="token已經失效!")
        except jwt.DecodeError:
            raise rest_exception.AuthenticationFailed(code=status.HTTP_401_UNAUTHORIZED, detail="token認證失敗!")
        except jwt.InvalidTokenError:
            raise rest_exception.AuthenticationFailed(code=status.HTTP_401_UNAUTHORIZED, detail="token非法!")

第4步:源碼分析

局部認證:spa

在一個須要認證的CBV裏面,添加authentication_classes類屬性。如:3d

class UserAPIView(GenericAPIView, ListModelMixin):
    authentication_classes = [MyAuthentication]
    queryset = User.objects
    serializer_class = UserSerializer

全局認證:rest

settings.py裏面設置:code

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["libs.MyAuth.MyAuthentication"]
}

在源碼分析部分,你們將會明白爲什麼這樣設置。jwt

源碼分析

認證的執行,是發生在dispatch函數的過程當中。

image-20201230142921787

值得注意的是,封裝request的時候,咱們的指定的認證類也會一塊兒封裝在新的request裏面。

image-20201230144040055

接下來看看get_authenticators的執行。

image-20201230144621995

使用列表生成式挨個實例化了每一個authentication_classes裏面的認證類。而authentication_classes讀取了咱們自定義的認證類。

注:

若是是局部認證,那麼就是咱們直接給authentication_classes賦值

若是是全局認證,那麼就是讀取咱們settings中的DEFAULT_AUTHENTICATION_CLASSES配置項。

image-20201230144321708

以後,在initial函數中,執行了三大驗證,其中就有認證。

image-20201230143144627

而認證直接執行了request.user,它實際上是一個被@property裝飾的方法。

image-20201230143539050

接下來的操做都是在rest_framework.request模塊裏面。新封裝的request就是這下面的Request類的實例。

image-20201230143610541

當沒有_user屬性的時候,說明還未認證,則會執行 _authenticate() 方法

image-20201230145633396

  • 認證成功,返回元組。
  • 認證失敗,執行_not_authenticated

補充

最後,一個問題,當配置了全局認證之後,以後每一個接口的訪問都要執行認證,而有的藉口並不須要認證或者認證的類不同(如登錄接口),該怎麼辦呢?

其實很好辦,全局設置之後並不影響局部設置的生效,由於局部設置的優先級大於全局設置,就好比對於登錄接口來講,咱們只須要在登錄接口CBV中設置authentication_classes爲空就能夠了。若是有特殊須要,也能夠指定其餘認證類。

class LoginAPIView(APIView):
    authentication_classes = []
相關文章
相關標籤/搜索