关于drf框架中的用户认证

2023年 9月 25日 87.2k 0

本文介绍关于drf框架中的用户认证的源码流程

在drf框架中我们通过相关组件来实现用户认证,下面是导包的语句

from rest_framework.authentication import BaseAuthentication

这段代码我来判断用户的请求头中是否传入了token信息来进行用户验证,当然这里可以自行修需要验证的内容

class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token=request.query_params.get("token")
        print(token)
        if not token:
            return
        user_object=models.Userinfo.objects.filter(token=token).first()
        if user_object:
            return user_object,token
        return

这段代码来判断请求头中有没有token

# 判断请求头是否有token
class HeaderAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token= request.META.get('HTTP_AUTHORIZATION')
        if not token:
            return
        user_object=models.Userinfo.objects.filter(token=token).first()

        if user_object:
            return user_object,token
        return

下面这段代码是我们判断如果没有通过验证,将会返回某些认证信息,这里需要导入AuthenticationFailed包

class NoAuthentication(BaseAuthentication):
    def authenticate(self, request):
        raise AuthenticationFailed({"code":10001,"msg":"认证失败"})

下面是思路梳理
一切的一切开始都从url开始,我们首先调用了as_view()方法,那么就仅需里面看看发生了什么

def as_view(cls, **initkwargs):
    """
    Store the original class on the view function.

    This allows us to discover information about the view when we do URL
    reverse lookups.  Used for breadcrumb generation.
    """
    if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
        def force_evaluation():
            raise RuntimeError(
                'Do not evaluate the `.queryset` attribute directly, '
                'as the result will be cached and reused between requests. '
                'Use `.all()` or call `.get_queryset()` instead.'
            )
        cls.queryset._fetch_all = force_evaluation

    view = super().as_view(**initkwargs)
    view.cls = cls
    view.initkwargs = initkwargs

    # Note: session based authentication is explicitly CSRF validated,
    # all other authentication is CSRF exempt.
    return csrf_exempt(view)

这里返回view,从17行我们可以知道他调用了父类的方法,进去查看一下

def as_view(cls, **initkwargs):
    """Main entry point for a request-response process."""
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError(
                "The method name %s is not accepted as a keyword argument "
                "to %s()." % (key, cls.__name__)
            )
        if not hasattr(cls, key):
            raise TypeError(
                "%s() received an invalid keyword %r. as_view "
                "only accepts arguments that are already "
                "attributes of the class." % (cls.__name__, key)
            )

    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        self.setup(request, *args, **kwargs)
        if not hasattr(self, "request"):
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
        return self.dispatch(request, *args, **kwargs)

    view.view_class = cls
    view.view_initkwargs = initkwargs

    # __name__ and __qualname__ are intentionally left unchanged as
    # view_class should be used to robustly determine the name of the view
    # instead.
    view.__doc__ = cls.__doc__
    view.__module__ = cls.__module__
    view.__annotations__ = cls.dispatch.__annotations__
    # Copy possible attributes set by decorators, e.g. @csrf_exempt, from
    # the dispatch method.
    view.__dict__.update(cls.dispatch.__dict__)

    # Mark the callback if the view class is async.
    if cls.view_is_async:
        markcoroutinefunction(view)

    return view

他返回的view值在第24行的dispatch里,这里我们来到他的apiview里面去查找dispatch方法
(注意这里的dispatch方法有很多都有,我们要看看是谁调用的这个方法,这里涉及面向对象的知识,不懂的可以去回顾一下)

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 = 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

        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封装被封装成了一个新的对象,不同于原来的django中的request,现在封装成新的request对象,继承了原来的方法,但是也增加了新的内容,这里感兴趣的可以自己看看,这里不再说明,接着他在第13行进行了一个定义方法,我们进去查看一下


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.
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    # Ensure that the incoming request is permitted
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)

(这里的17,18,19行分别是认证组件,权限组件,节流组件)
进入17行我们来看一下

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是什么
我们进去,发现def user函数,这里有个装饰器,不懂得再去回顾回顾,主要是这样就能够直接调用这个函数,同理这里我们也找到了auth方法,所以我们能够直接调用request.auth和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()
    return self._user
@property
def auth(self):
    """
    Returns any non-user authentication information associated with the
    request, such as an authentication token.
    """
    if not hasattr(self, '_auth'):
        with wrap_attributeerrors():
            self._authenticate()
    return self._auth

self._authenticate()这个方法是什么呢,我们进去看看

def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    for authenticator in self.authenticators:
        try:
            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()

这里的authenticators是我们创建的各个认证类,他通过循环遍历,然后查找authenticate方法,这里要求返回user,和auth两个参数,所以也就是我们一开始需要两个返回值的原因,之后便可以通过调用request.auth和request.user来获取数据了

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论