• restful framework 认证源码流程


    Django-Rest-Framework源码流程

    请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法

    注意:APIView中的dispatch方法有很多功能
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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 = 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:
    handler = getattr(self, request.method.lower(),
    self.http_method_not_allowed)
    else:
    handler = self.http_method_not_allowed

    # 第三步、执行:get/post/put/delete函数
    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

    上面是大致步骤,下面我们来具体分析一下,看每个步骤中都具体干了什么事

    对request进行加工(添加数据)

    我们看看request里面都添加了那些数据

    a

    首先  request = self.initialize_request(request, *args, **kwargs)
    
    点进去,会发现:在Request里面多加了四个,如下
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    #吧请求弄成一个字典返回了
    parser_context = self.get_parser_context(request)

    return Request(
    request,
    parsers=self.get_parsers(), #解析数据,默认的有三种方式,可点进去看
    #self.get_authenticator优先找自己的,没有就找父类的
    authenticators=self.get_authenticators(), #获取认证相关的所有类并实例化,传入request对象供Request使用
    negotiator=self.get_content_negotiator(),
    parser_context=parser_context
    )

    b

    获取认证相关的类的具体   
    authenticators=self.get_authenticators()
    
    1
    2
    3
    4
    5
    6
    def get_authenticators(self):
    """
    Instantiates and returns the list of authenticators that this view can use.
    """
    #返回的是对象列表
    return [auth() for auth in self.authentication_classes] #[SessionAuthentication,BaseAuthentication]

    c

    查看认证的类:self.authentication_classes
    
    1
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES  #默认的,如果自己有会优先执行自己的

    d

    接着走进api_settings
    
    1
    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)  #点击继承的DEFAULTS类
    1
    2
    3
    4
    5
    6
    DEFAULTS = {
    # Base API policies
    'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework.authentication.SessionAuthentication', #这时候就找到了他默认认证的类了,可以导入看看
    'rest_framework.authentication.BasicAuthentication'
    ),

    e

    导入了类看看类里面具体干了什么
    
    1
    2
    from rest_framework.authentication import SessionAuthentication
    from rest_framework.authentication import BaseAuthentication

    f

    看到里面有个authenticate方法和authenticate_header方法
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    class BaseAuthentication(object):
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
    """
    Authenticate the request and return a two-tuple of (user, token).
    """
    raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
    """
    Return a string to be used as the value of the `WWW-Authenticate`
    header in a `401 Unauthenticated` response, or `None` if the
    authentication scheme should return `403 Permission Denied` responses.
    """
    pass

    具体处理认证,从headers里面能获取用户名和密码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    class BasicAuthentication(BaseAuthentication):
    """
    HTTP Basic authentication against username/password.
    """
    www_authenticate_realm = 'api'

    def authenticate(self, request):
    """
    Returns a `User` if a correct username and password have been supplied
    using HTTP Basic authentication. Otherwise returns `None`.
    """
    auth = get_authorization_header(request).split()

    if not auth or auth[0].lower() != b'basic':
    return None #返回none不处理。让下一个处理

    if len(auth) == 1:
    msg = _('Invalid basic header. No credentials provided.')
    raise exceptions.AuthenticationFailed(msg)
    elif len(auth) > 2:
    msg = _('Invalid basic header. Credentials string should not contain spaces.')
    raise exceptions.AuthenticationFailed(msg)

    try:
    auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':') #用partition切割冒号也包括
    except (TypeError, UnicodeDecodeError, binascii.Error):
    msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
    raise exceptions.AuthenticationFailed(msg)

    userid, password = auth_parts[0], auth_parts[2] # 返回用户和密码
    return self.authenticate_credentials(userid, password, request)

    def authenticate_credentials(self, userid, password, request=None):
    """
    Authenticate the userid and password against username and password
    with optional request for context.
    """
    credentials = {
    get_user_model().USERNAME_FIELD: userid,
    'password': password
    }
    user = authenticate(request=request, **credentials)

    if user is None:
    raise exceptions.AuthenticationFailed(_('Invalid username/password.'))

    if not user.is_active:
    raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

    return (user, None)

    def authenticate_header(self, request):
    return 'Basic realm="%s"' % self.www_authenticate_realm

    g

    当然restfulframework默认定义了两个类。我们也可以自定制类,
    自己有就用自己的了,自己没有就去找父类的了,
    但是里面必须实现authenticate方法,不然会报错。
    

    进行以下操作

    • 处理版权信息
    • 认证
    • 权限
    • 请求用户进行访问频率的限制

    我们主要来看一下认证流程:

    a

    首先 self.initial(request, *args, **kwargs)可以看到做了以下操作
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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.
    #2.1 处理版本信息
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    # Ensure that the incoming request is permitted
    #2.2 认证
    self.perform_authentication(request)
    # 2.3 权限
    self.check_permissions(request)
    # 2.4 请求用户进行访问频率的限制
    self.check_throttles(request)

    b

    我们先来看认证,self.perform_authentication(request) 
    具体干了什么,按住ctrl点击进去
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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已经是加工后的request了

    c

    那么我们可以从视图里面导入一下Request,找到request对象的user方法
    
    1
    from rest_framework.views import Request
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @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 #返回user

    d

    执行self._authenticate() 开始用户认证,
    如果验证成功后返回元组: (用户,用户Token)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    #循环对象列表
    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 #返回一个元组,user,和auth,赋给了self,
    # 只要实例化Request,就会有一个request对象,就可以request.user,request.auth了
    return

    self._not_authenticated()

    e

    在user_auth_tuple = authenticator.authenticate(self) 进行验证,
    如果验证成功,执行类里的authenticatie方法 
    

    f

    如果用户没有认证成功:self._not_authenticated()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def _not_authenticated(self):
    """
    Set authenticator, user & authtoken representing an unauthenticated request.

    Defaults are None, AnonymousUser & None.
    """
    #如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
    self._authenticator = None #

    if api_settings.UNAUTHENTICATED_USER:
    self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户AnonymousUser
    else:
    self.user = None # None 表示跳过该认证

    if api_settings.UNAUTHENTICATED_TOKEN:
    self.auth = api_settings.UNAUTHENTICATED_TOKEN() # 默认值为:None
    else:
    self.auth = None

    # (user, token)
    # 表示验证通过并设置用户名和Token;
    # AuthenticationFailed异常

    执行get/post/delete等方法

    对返回结果在进行加工

  • 相关阅读:
    前台提交数据到后台还回错误提示并且把新增页面关掉的问题
    combox二级联动带不出来问题
    数据库设计字段如果首字母是大写
    带图片的修改后台出现的问题
    weblogic启动报错
    强制删除文件夹
    jQuery常用方法
    json数据的转换
    SQL DEFAULT 约束
    SQL CHECK 约束
  • 原文地址:https://www.cnblogs.com/xiao-xue-di/p/9872780.html
Copyright © 2020-2023  润新知