zoukankan      html  css  js  c++  java
  • 认证功能及源码流程

    安装

    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(内部循环 ...)
  • 相关阅读:
    系统管理员必须掌握的20个Linux监控工具
    JavaWeb基础—MVC与三层架构
    JavaWeb基础—JavaBean
    JavaWeb基础—JSP
    myeclipse(eclipse)IDE配置
    JavaWeb基础—会话管理之Session
    JavaWeb基础—会话管理之Cookie
    JavaWeb基础—项目名的写法
    JavaWeb基础—HttpServletRequest
    JavaWeb基础—VerifyCode源码
  • 原文地址:https://www.cnblogs.com/qiuxirufeng/p/10432888.html
Copyright © 2011-2022 走看看