drf重寫authenticate方法實現多條件登入(原始碼分析)

2023-05-11 06:00:38

drf重寫authenticate方法實現多條件登入(原始碼分析)

1. 思路

JWT拓展的登入檢視中, 在接受到使用者名稱和密碼時, 呼叫的也是Django的認證系統中提供的authenticate()來檢查使用者名稱與密碼是否正確.

我們可以通過修改Django系統的認證後端來支援登入賬號既可以是使用者名稱也可以是手機號

修改重寫思路是:

修改Django認證系統的認證後端需要繼承django.contrib.auth.backends.ModelBackends, 並重寫authenticate方法

我們需要重寫什麼位置?

因為我們的需求是多條件登入, 所以我們就應該著手於資料庫校驗操作, 尋找定位原始碼中資料庫校驗username是否存在的操作, 並使用Q查詢新增多個條件驗證

2. 原始碼分析

下面是以from rest_framework_jwt.views import obtain_jwt_token為入口進行的原始碼分析(序號為查詢流程):

  • 繼承JsonWebTokenAPIView檢視, 當接受到使用者post請求時, 執行get_serializer()

  • 呼叫get_serializer()方法找到子類中定義的serializer類

  • 當呼叫is_valid()方法時, 會呼叫到validate()

  • 這裡呼叫了authenticate方法並傳入了使用者資料, 返回的物件user

  • authenticate方法中呼叫了_get_backneds()方法, 此方法中將組態檔AUTHENTICATION_BACKENDS讀取, 這個是django預設的設定, 我們重寫方法之後, 需要將我們的類也設定到這裡
  • django的預設組態檔路徑 django>conf>global_settings.py>AUTHENTICATION_BACKENDS
  • 讀取了組態檔之後呼叫到了ModelBackend中的authenticate方法
  • authenticate方法
    • 獲取username
    • 攜帶username進行查表操作, 返回user物件
    • 如果查到user,就去驗證其密碼的正確性
  • 所以我們就可以重寫的ModelBackend中的authenticate方法(修改資料庫操作)

3. 程式碼實現

在utils/authenticate.py中:

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q


def get_user_by_account(account):

    """
    根據帳號資訊獲取user模型範例物件
    :param account: 賬號資訊,可以是使用者名稱,也可以是手機號,甚至其他的可用於識別使用者身份的欄位資訊
    :return: User物件 或者 None
    """
    user = UserModel.objects.filter(Q(mobile=account) | Q(username=account) | Q(email=account)).first()
    return user


class CustomAuthBackend(ModelBackend):
    """
    自定義使用者認證類[實現多條件登入]
    """
    def authenticate(self, request, username=None, password=None, **kwargs):
        """
        多條件認證方法
        :param request: 本次使用者端的http請求物件
        :param username:  本次使用者端提交的使用者資訊,可以是user,也可以mobile或其他唯一欄位
        :param password: 本次使用者端提交的使用者密碼
        :param kwargs: 額外引數
        :return:
        """
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)

        if username is None or password is None:
            return
        # 根據使用者名稱資訊useranme獲取賬戶資訊
        user = get_user_by_account(username)
        if user and user.check_password(password) and self.user_can_authenticate(user):
            return user

在組態檔settings/dev.py中告知Django使用我們自定義的認證後端,注意不是給drf新增設定。

# django自定義認證

AUTHENTICATION_BACKENDS = ['luffycityapi.utils.authenticate.CustomAuthBackend', ]