rest-framework源码解析--SessionAuthentication

ok,又到了看源码的时间了,今天我们看的源码是 SessionAuthentication 类


这一源码函数较少,比较简单,首先看到 authenticate 函数:

    def authenticate(self, request):
        """
        Returns a `User` if the request session currently has a logged in user.
        Otherwise returns `None`.
        """
        # Get the session-based user from the underlying HttpRequest object
        user = getattr(request._request, 'user', None)

        # Unauthenticated, CSRF validation not required
        if not user or not user.is_active:
            return None

        self.enforce_csrf(request)

        # CSRF passed with authenticated user
        return (user, None)

多行注释的译文大概是:

  •         如果请求会话当前有登录用户,则返回“User”。
  •         否则返回“None”。

好了,清晰明了,这一段解释的是 user = getattr(request._request, 'user', None) 这一语句,

request._request 来自于 site-packages \ rest_framework \ request.py,它在文件中的语句是:

rest-framework源码解析--SessionAuthentication 

 科普一下getattr() 函数的用法:getattr(object, name[, default])

在这里,我们通过使用 getattr 来获取 request 中的 user 属性,如果有这一属性,说明用户已经登录了,将 ‘user’ 属性的值 传递给 user,如果没有这一属性,说明用户未登录,传递 None。继续往下看:

    def authenticate(self, request):
        """
        Returns a `User` if the request session currently has a logged in user.
        Otherwise returns `None`.
        """
        # Get the session-based user from the underlying HttpRequest object
        user = getattr(request._request, 'user', None)

        # Unauthenticated, CSRF validation not required
        if not user or not user.is_active:
            return None

        self.enforce_csrf(request)

        # CSRF passed with authenticated user
        return (user, None)

 if 语句可以忽略不看,大意是:

==>如果 用户不存在 或者 用户处于 非活跃状态

==>        返回 空

接着我们可以先不看 self.enforce_csrf(request)

最后,返回 user


 再往下看:self.enforce_csrf(request),找到对应的函数: 

    def enforce_csrf(self, request):
        """
        Enforce CSRF validation for session based authentication.
        """
        def dummy_get_response(request):  # pragma: no cover
            return None

        check = CSRFCheck(dummy_get_response)
        # populates request.META['CSRF_COOKIE'], which is used in process_view()
        check.process_request(request)
        reason = check.process_view(request, None, (), {})
        if reason:
            # CSRF failed, bail with explicit error message
            raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)

 首先是 dummy_get_response 函数,这个函数直接看翻译就能理解它的作用 ( 假装获取响应 ),返回一个空值。

再往下看: CSRFCheck(dummy_get_response)

class CSRFCheck(CsrfViewMiddleware):
    def _reject(self, request, reason):
        # Return the failure reason instead of an HttpResponse
        return reason

reject (拒绝) 函数,返回一个 reason (解释)

这里没什么意思,回过头来看下一句:check.process_request(request),

找到 process_request 函数:

    def process_request(self, request):
        csrf_token = self._get_token(request)
        if csrf_token is not None:
            # Use same token next time.
            request.META['CSRF_COOKIE'] = csrf_token

好,重点来了,敲黑板,我们找到 _get_token 函数:

    def _get_token(self, request):
        if settings.CSRF_USE_SESSIONS:
            try:
                return request.session.get(CSRF_SESSION_KEY)
            except AttributeError:
                raise ImproperlyConfigured(
                    'CSRF_USE_SESSIONS is enabled, but request.session is not '
                    'set. SessionMiddleware must appear before CsrfViewMiddleware '
                    'in MIDDLEWARE.'
                )
        else:
            try:
                cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
            except KeyError:
                return None

            csrf_token = _sanitize_token(cookie_token)
            if csrf_token != cookie_token:
                # Cookie token needed to be replaced;
                # the cookie needs to be reset.
                request.csrf_cookie_needs_reset = True
            return csrf_token

乍一看,好像有很多的内容,实际上,我们根据 CSRF_USE_SESSIONS 将它拆开来看:

  •  如果 CSRF_USE_SESSIONS 启用,我们返回 session 中的 ‘_csrftoken’ 属性 的值
CSRF_SESSION_KEY = '_csrftoken'
  •  如果 CSRF_USE_SESSIONS 未启用,返回一个 csrf_token

 这一块不太好说明,我修改了部分后端代码,以及在源码中加了点东西以方便我们更直观的看到效果,直接上图:

rest-framework源码解析--SessionAuthentication

 这里我尝试的是:曾经登录过的用户在未登录的情况下访问页面,打印出来的数据,也就是请求头中的 Cookie.csrftoken 的数据是上一次登录时产生的 ‘csrf_token’

rest-framework源码解析--SessionAuthentication 

 接着是登录后:

rest-framework源码解析--SessionAuthentication

 可以很明显的看出 _get_token 这一函数只是为了新建一个 csrf_token ,回过头,我们再看一下 _sanitize_token 函数,直译过来是 ‘审查令牌’ 意思。

def _sanitize_token(token):
    # Allow only ASCII alphanumerics
    if re.search('[^a-zA-Z0-9]', token):
        return _get_new_csrf_token()
    elif len(token) == CSRF_TOKEN_LENGTH:
        return token
    elif len(token) == CSRF_SECRET_LENGTH:
        # Older Django versions set cookies to values of CSRF_SECRET_LENGTH
        # alphanumeric characters. For backwards compatibility, accept
        # such values as unmasked secrets.
        # It's easier to mask here and be consistent later, rather than add
        # different code paths in the checks, although that might be a tad more
        # efficient.
        return _mask_cipher_secret(token)
    return _get_new_csrf_token()

 这里跟你们说明一下:

  • CSRF_SECRET_LENGTH = 32
  • CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH

以及  _get_new_csrf_token() 和 _mask_cipher_secret() 函数,他们内部的函数调用是相仿的,最终都是返回一个 ‘令牌’,算是 django 中的一种向后兼容的方式。


Session 验证类 概述:

        在我们使用该类时,会筛选掉一批非法用户(无用户,非活跃用户),防止为其创建 csrf_token ,接着,就会通过其中的 self.enforce_csrf(request) 语句来为当前登录用户创建一个 csrf_token 并赋值给 CSRF_COOKIE 用来应对后续的验证。

好了,关于 SessionAuthentication 的源码部分就到这里了。

#Ps:属实是有些云里雾里的感觉,后续有机会再琢磨琢磨

上一篇:第七节 CSRF跨站请求伪造


下一篇:登录系统中token和session的比较