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
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...