Django restframework用戶登錄認證組件增加及源碼分析

来源:https://www.cnblogs.com/aitiknowledge/archive/2020/05/31/13022496.html
-Advertisement-
Play Games

用戶驗證源碼剖析,註意:一定要看代碼的中文註釋及其下麵的一行代碼!!! 1、準備一個路由和視圖類,全局路由配置暫時忽略,當流程執行到下麵的url:groupsSelectAll——> GroupsView的視圖類下的as_view()方法 from django.conf.urls import u ...


用戶驗證源碼剖析,註意:一定要看代碼的中文註釋及其下麵的一行代碼!!!

1、準備一個路由和視圖類,全局路由配置暫時忽略,當流程執行到下麵的url:groupsSelectAll——> GroupsView的視圖類下的as_view()方法

from django.conf.urls import url

from . import views


app_name = '[words]'
urlpatterns = [
    url(r'groupsSelectAll/', views.GroupsView.as_view(), name="groupsSelectAll"),   # 片語信息查詢所有

]
class GroupsView(APIView):

    def get(self, request):
        conditions = {
            "id": request.query_params.get("wid"),
            "name": request.query_params.get("name"),
            "start_time": request.query_params.get("start_time"),
            "end_time": request.query_params.get("end_time"),
        }
        res = DataManager.select_by_conditions("words_groups", None, **conditions)
        return Response(data={"code": 200, "result": res})

2、但是GroupsView類下沒有as_view方法,我們就要去它的父類APIView查看(我們點進去看as_view方法),這裡我只複製方法源代碼,大家只需要看中文註釋及其下的代碼語句。在這個方法中值得一提的是super關鍵字,如果請求視圖類(就是GroupsView類,如果繼承了多個父類)還有另一個父類,它先會查看這個父類是否有as_view方法。在這裡它是會執行APIView的父類View中的as_view方法,然後我們再次查看父類View的as_view方法。第一個as_view方法是APIView類的,第二個as_view方法是View類的。

    @classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        # 執行父類的as_view方法
        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)
APIView.as_view()
    @classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        # 執行view方法
        def view(request, *args, **kwargs):
            # 這裡的cls就是我們的請求視圖類,顯而易見,這個self是請求試圖類的對象
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            # 然後這裡就是執行dispatch方法
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
View.as_view()

3、我們在第二個as_view方法中可以知道self是我們的請求視圖類的對象,通過這個self調用dispatch方法,請求視圖類中沒有dispatch方法,是不是又去APIView類中執行dispatch方法。

def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        # 這裡是對原生的request加工處理,返回一個新的request對象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            # 初始化(用戶登錄認證,許可權驗證,訪問頻率限制)
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                # 通過python的反射機制反射到請求視圖類的方法名稱
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            
            # 最後就是執行請求視圖類的方法
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
APIView.dispatch()

4、在方法中先封裝了原生的request對象,我們可以點進去查看新的request在原來的request的改進之處:先是封裝了原生的request對象,然後又加了用戶登錄驗證的“類”,現在慢慢進入了主題。以下添加APIView類的部分代碼,也就是新的request對象用到的代碼。

class APIView(View):

    # The following policies may be set at either globally, or per-view.
    # 如果請求視圖類中沒有這個屬性,我們就使用預設的用戶登錄驗證類
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)
        
        # 封裝成新的request對象
        return Request(
            # 原生的request
            request,
            parsers=self.get_parsers(),
            # 點擊查看用戶認證的列表是什麼
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        # 返回的是一個用戶認證的對象,是一個列表的形式
        # 而且這個self.authentication_classes的值是先從請求視圖類中尋找,再而從父類尋找
        return [auth() for auth in self.authentication_classes]
APIView

5、這個類中的self也是請求視圖類的對象,如果self(請求試圖類的對象)沒有authentication_classes變數就會使用全局的配置文件中尋找,就是settings.py文件需要添加一個字典REST_FRAMEWORK={"DEFAULT_AUTHENTICATION_CLASSES":["類的全路徑"]}類似的。這裡我們假設全局配置文件中用戶登錄驗證類或者我們自定義的用戶登錄驗證類並且添加進去了。執行完下麵的代碼,我們上面的APIView.dispatch()方法中的self.request已經變成了新的request對象,我們接著往下看。

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        # 用戶驗證的方法,這個request 是加工之後的request
        self.perform_authentication(request)
        # 用戶許可權驗證
        self.check_permissions(request)
        # 用戶訪問頻率限制
        self.check_throttles(request)
APIView.initial()

5、這就到了我們的用戶登錄驗證的戲碼了。我們可以查看代碼中的self.perform_authentication(request),裡面就是一行代碼request.user(這裡我不添加了),我們可以直接點進去這個user方法的實現,這裡直接添上Request類的部分代碼,也就是user相關的代碼。

class Request(object):
    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                # 查看用戶登錄的驗證操作
                self._authenticate()
        return self._user

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        # 這個self.authenticators是加工後request的,值是用戶登錄驗證的對象列表
        for authenticator in self.authenticators:
            try:
                # 這個authenticate方法我們就可以在自定義的類中編寫,
                # 主要功能可以查看用戶是否登錄驗證(可根據資料庫中的數據)
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                # 拋出異常時執行方法
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        # 全局配置文件沒有配置和請求試圖類中都沒有authentication_classes變數及值
        self._not_authenticated()

    def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        self._authenticator = None

        # 如果全局配置文件的REST_FRAMEWORK中沒有都是預設None
        if api_settings.UNAUTHENTICATED_USER:
            self.user = api_settings.UNAUTHENTICATED_USER()
        else:
            self.user = None

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()
        else:
            self.auth = None
Request

6、在Request類中會執行到user_auth_tuple = authenticator.authenticate(self),這個authenticator就是我們全局配置的用戶登錄類或者是請求視圖類的authentication_classes變數列表元的對象,執行裡面的authenticate方法,我們可以直接點進去查看authenticate方法。一般我們自定義這個用戶登錄驗證類的話我們一般需要繼承BaseAuthentication類,這樣我們直接重寫authenticate方法,裡面的需求就是驗證時候登錄攜帶了token信息或者session信息又或者前後端定義的標準信息(只要是能識別用戶登錄了的信息即可)。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 不管你是做哪個語言的,只要是個程式員都懂: 哈哈啥哈哈哈 來跟我一起學習java吧,做個牛b的程式員。我們一起來敲bug吧 “大清亡於閉關鎖國,學習技術需要交流和資料”。 在這裡我給大家準備了很多的學習資料免費獲取,包括但不限於技術乾貨、大廠面試題系列、技術動向、職業生涯等一切有關程式員的分享. w ...
  • # 5.Utilities:工具 - 1. 邊框 中文網站邊框部分:http://bs4.vx.link/index.html?tmpui_page=/pages/utilities - 2. 清除浮動 - 3. 關閉圖標 - 4. 顏色 - 5. 顯示 3.x版本顯示只有三種:block, inl ...
  • 一、 1、打開方式 打開Chrome瀏覽器,按下F12或者右擊空白處然後點擊檢查 最左邊是顯示效果,中間是html代碼,右邊是html樣式。 2、樣式的修改 點擊中間代碼框,左上角的小箭頭,然後點擊css樣式,可以直接修改屬性的值。也可以點擊鍵盤上的上下箭頭,對屬性的值進行修改 需要註意的是,調試工 ...
  • 平時常用的一些功能性函數 關於原生JS 文件大小單位轉換 /** * @desc bytesToSize 位元組單位換算 * @param bytes 傳入以bit為單位的數據 */ const bytesToSize = function (bytes) { const k = 1024; if ( ...
  • Command模式屬於行為模式,作為大名鼎鼎的23個設計模式之一,Command模式理解起來不如工廠模式,單例模式等那麼簡單直白。究其原因,行為模式著重於使用,如果沒有編程實踐,確實不如創造模式那麼直白。我們先看看UML類圖。 估計很多同學看著圖就暈了,那麼多東西,Command和Concrete ...
  • J2SE(Java 2 Platform Standard Edition) J2ME(Java 2 Platform Micro Edtion) J2EE(Java 2 Platform Enterprise Edition) Java語言特點:開源 跨平臺(JVM) 面向對象等 JRE與JDK ...
  • Java生鮮電商平臺-生鮮系統代碼審查以及優化方案(小程式/APP) 說明:Java生鮮電商平臺-生鮮系統代碼審查以及優化方案(小程式/APP) 代碼審查也就是我們常說的:CodeReview,常見的生鮮電商小程式或者APP中CodeReview有以下的規範與目標: 1. 目標和原則 提高代碼質量, ...
  • 一、獲取某個特定的方法,通過​反射機制。 package com.bjpowernode.java_learning; import java.lang.reflect.*; ​ public class D119_1_ReflectMethodOfSpecialClass { public sta ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...