認證功能及源碼流程

安裝

pip install djangorestframework

urls.pyhtml

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^dog/', views.DogView.as_view())
]

views.pypython

from rest_framework.views import APIView

class DogView(APIView):
    
    def get(self, request, *args, **kwargs):
        # 這裏的 request 是rest framework加工以後的 request,再也不是原來的request了
        print(request)
        ret = {
            'code': 1000,
            'msg': 'xxx'
        }
        return HttpResponse(json.dumps(ret), status=201)

    def post(self, request, *args, **kwargs):
        return HttpResponse('建立Dog')

    def put(self, request, *args, **kwargs):
        return HttpResponse('更新Dog')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('刪除Dog')

源碼流程

封裝 request

當請求進來,執行 dispatch,本身沒有找父類 APIViewdispatchdjango

def dispatch(self, request, *args, **kwargs):
    ... # 省略的內容
    self.kwargs = kwargs
    
    # 對原生的request進行加工(豐富了一些功能)
    '''
    # 鼠標點進 initialize_request,返回一個對象
    return Request(
        request,    # 這裏纔是原生的request
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
    '''
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    ...
def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)
    
    # 返回Request類的一個對象,交給上面的request
    return Request(
        request,    # 這裏纔是原生的request
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(), # 這裏有個 get_authenticators
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

問題:若是執行 self.get_authenticators() ,流程是怎樣的,哪裏是入口?json

'''
應該從 DogView 作入口,由於裏面全部傳過去的self,都是 DogView 的對象
DogView 沒有再往父類找
這裏 DogView 沒有 get_authenticators(),而且這個方法應該加了s,猜想應該是個複數
鼠標點進去,查看父類
'''
def get_authenticators(self):
    """
    Instantiates and returns the list of authenticators that this view can use.
    """
    return [auth() for auth in self.authentication_classes]

'''
能夠看到,返回了一個列表,若是 self.authentication_classes 也是一個列表,而且
self.authentication_classes = [Foo, Bar]
列表裏面是兩個類,則 
[auth() for auth in self.authentication_classes] 就是對每一個類進行實例化
因此誰調用這個 get_authenticators 方法,返回的就是 [Foo, Bar] 的對象
即 [Foo(), Bar()]

固然這些目前只是猜想

'''

根據以上猜想,來查看一下api

'''
這裏執行的是 self.authentication_classes,DogView 沒有,去父類查看
'''
class APIView(View):
    ... # 其餘的內容
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    ...

'''
這實際上是讀取了 rest_framework的配置文件,是在 APIView中寫的
找的時候優先到 DogView 中查找,沒有再去父類
'''

如今爲 DogView 加上 authentication_classes ,用的就是 DogView 的方法ide

from rest_framework.views import APIView
from rest_framework.authentication import BasicAuthentication

class DogView(APIView):

    authentication_classes = [BasicAuthentication, ]    # 增長的
    
    def get(self, request, *args, **kwargs):
        self.dispatch()
        ret = {
            'code': 1000,
            'msg': 'xxx'
        }
        return HttpResponse(json.dumps(ret), status=201)

    def post(self, request, *args, **kwargs):
        return HttpResponse('建立Dog')

    def put(self, request, *args, **kwargs):
        return HttpResponse('更新Dog')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('刪除Dog')

如今增長了 authentication_classes,回到上面的 get_authenticators方法,它返回的是 [BasicAuthentication(), ] 對象,再將它交給 Request 對象post

因此新的 Request 對象目前封裝了兩個值,一個是原生的 request ,一個是 [BasicAuthentication(),] 對象列表this

回到 dispatchurl

def dispatch(self, request, *args, **kwargs):
    ... # 省略的內容
    self.kwargs = kwargs
    
    # 對原生的request進行加工(豐富了一些功能)
    '''
    # 鼠標點進 initialize_request,返回一個對象
    return Request(
        request,    # 這裏纔是原生的request
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
    
    這裏就是
    return Request(request, [BasicAuthentication(), ])
    '''
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    ... # 省略的內容

如今是封裝進去了,那麼要取出,該怎麼取呢?(能夠經過 request 點出某個屬性或方法)能夠查看一下 Requestspa

class Request(object):
    ... # 省略的內容

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request  # 若是要拿原生的request,須要執行 request._request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        ... # 省略的內容
# 再回到 dispatch()

def dispatch(self, request, *args, **kwargs):
    ... # 省略的內容
    self.kwargs = kwargs
    
    # 對原生的request進行加工(豐富了一些功能)
    '''
    # 鼠標點進 initialize_request,返回一個對象
    return Request(
        request,    # 這裏纔是原生的request
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
    
    這裏就是
    return Request(request, [BasicAuthentication(), ])
    '''
    request = self.initialize_request(request, *args, **kwargs)
    # 獲取原生的request: request._request
    # 獲取認證類的對象: request.authenticators
    
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        # 將上面的 request 放進來,執行initial,DogView沒有,去父類查找
        self.initial(request, *args, **kwargs)

        ... # 省略的內容

認證

# 來到 initial

# 這裏的 request 是封裝後的 request
def initial(self, request, *args, **kwargs):
    
    ... # 省略的內容    
    
    # 這裏的request是帶有原生的request和認證的對象的request
    # 執行 perform_authentication,DogView 沒有,去父類查找
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)
# 來到 perform_authentication

def perform_authentication(self, request):
    """
    Perform authentication on the incoming request.

    Note that if you override this and simply 'pass', then authentication
    will instead be performed lazily, the first time either
    `request.user` or `request.auth` is accessed.
    """
    
    # 去加工後的request中找 .user
    request.user
# 經過Request實例化後的對象找到 .user

@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()    # 注意這裏,去 _authenticate
        return self._user
# 來到 _authenticate

def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    # 這裏的 self.authenticators 就是加工後的request中的第二個值,
    # 也就是 [BasicAuthentication對象, ]
    # 這一步就是循環認證類的全部對象
    for authenticator in self.authenticators:
        try:
            # 拿到上面的[BasicAuthentication對象, ],裏面有個 .authenticate 方法
            # 若是這個方法拋出異常,就會被下面的except捕獲到
            # 若是沒有異常,則有返回值(返回值有兩種,一個是None,一個是有確切的值)
            # 有確切的返回值,必須是元組:(request.user, request.auth)
            # 若是是None,表示本次認證對象不作處理,繼續作下一次的循環處理
            # 若是全部對象都爲None,那麼 request.user, request.auth均未賦值
            # 等循環玩走最後一步的 self._not_authenticated()
            
            # 括號裏的 self 是 request 對象,當前代碼是在 request.py 中
            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
        
    self._not_authenticated()   # 全部對象都爲None,走這一步
def _not_authenticated(self):
    """
    Set authenticator, user & authtoken representing an unauthenticated request.
    # 若是全部對象都爲None,這裏默認設置一個匿名用戶
    Defaults are None, AnonymousUser & None.
    """
    self._authenticator = None

    if api_settings.UNAUTHENTICATED_USER:
        self.user = api_settings.UNAUTHENTICATED_USER()     # AnonymousUser
    else:
        self.user = None

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

以上即是須要掌握的源碼流程,基於上面的思路,能夠作一個登陸認證的示例

登陸認證示例

總結

一、使用

  • 建立類,繼承 BaseAuthentication,實現 authenticate 方法
  • authenticate 方法有三種返回值
    • None,表示讓下一個認證來執行
    • 拋出 AuthenticationFailed 異常,表示認證失敗
    • 元組形式,(元素1, 元素2)元素1 賦值給 request.user元素2 賦值給 request.auth
  • 局部使用,在類中寫靜態字段,authentication_classes = [Authentication, ]
  • 全局使用,在配置文件中添加 REST_FRAMEWORK

二、源碼流程

​ 先走 dispatch() ,對 request 進行封裝,而後執行 initial,在 initial 中進行認證。執行認證的流程是去找 request.user ,在 request.user 中找以前封裝的 request 的全部認證對象,再循環全部的認證對象,執行 authenticate 方法

  • dispatch
    • 封裝 request
      • 獲取定義的認證類(全局 / 局部),經過列表生成式建立對象
    • initial
      • perform_authentication
        • request.user(內部循環 ...)
相關文章
相關標籤/搜索