认证功能及源码流程

安装

pip install djangorestframework

urls.py

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

views.py

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,自己没有找父类 APIViewdispatch

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() ,流程是怎样的,哪里是入口?

'''
应该从 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()]

当然这些目前只是猜测

'''

根据以上猜测,来查看一下

'''
这里执行的是 self.authentication_classes,DogView 没有,去父类查看
'''
class APIView(View):
    ...	# 其他的内容
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    ...

'''
这其实是读取了 rest_framework的配置文件,是在 APIView中写的
找的时候优先到 DogView 中查找,没有再去父类
'''

现在为 DogView 加上 authentication_classes ,用的就是 DogView 的方法

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 对象

所以新的 Request 对象目前封装了两个值,一个是原生的 request ,一个是 [BasicAuthentication(),] 对象列表

回到 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)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    ... # 省略的内容

现在是封装进去了,那么要取出,该怎么取呢?(可以通过 request 点出某个属性或方法)可以查看一下 Request

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

以上便是需要掌握的源码流程,基于上面的思路,可以做一个登录认证的示例

登录认证示例

总结

1、使用

  • 创建类,继承 BaseAuthentication,实现 authenticate 方法
  • authenticate 方法有三种返回值
    • None,表示让下一个认证来执行
    • 抛出 AuthenticationFailed 异常,表示认证失败
    • 元组形式,(元素1, 元素2)元素1 赋值给 request.user元素2 赋值给 request.auth
  • 局部使用,在类中写静态字段,authentication_classes = [Authentication, ]
  • 全局使用,在配置文件中添加 REST_FRAMEWORK

2、源码流程

​ 先走 dispatch() ,对 request 进行封装,然后执行 initial,在 initial 中进行认证。执行认证的流程是去找 request.user ,在 request.user 中找之前封装的 request 的所有认证对象,再循环所有的认证对象,执行 authenticate 方法

  • dispatch
    • 封装 request
      • 获取定义的认证类(全局 / 局部),通过列表生成式创建对象
    • initial
      • perform_authentication
        • request.user(内部循环 ...)
原文地址:https://www.cnblogs.com/qiuxirufeng/p/10432888.html