关于drf框架中的用户认证


prtyaa
prtyaa 2024-01-08 22:09:57 64506 赞同 0 反对 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来获取数据了

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
prtyaaL0
粉丝 1 资源 1949 + 关注 私信
最近热门资源
银河麒麟桌面操作系统V10SP1-2403-update1版本中,通过“麒麟管家-设备管理-硬件信息-硬盘”查看硬盘类型时,显示的是HDD(机械硬盘),而实际上该笔记本的硬盘类型为SSD  40
统信uos安装mysql的实例参考  31
分享解决宏碁电脑关机时自动重启的方法  30
在银河麒麟高级服务器操作系统V10SP3中,需要将默认shell类型修改为csh。  29
分享如何解决报错:归档 xxx.deb 对成员 control.tar.zst 使用了未知的压缩,放弃操作  28
统信uosboot区分未挂载导致更新备份失败  27
格之格打印机dp3300系列国产系统uos打印机驱动选择  25
以openkylin为例编译安装内核  23
最近下载排行榜
银河麒麟桌面操作系统V10SP1-2403-update1版本中,通过“麒麟管家-设备管理-硬件信息-硬盘”查看硬盘类型时,显示的是HDD(机械硬盘),而实际上该笔记本的硬盘类型为SSD 0
统信uos安装mysql的实例参考 0
分享解决宏碁电脑关机时自动重启的方法 0
在银河麒麟高级服务器操作系统V10SP3中,需要将默认shell类型修改为csh。 0
分享如何解决报错:归档 xxx.deb 对成员 control.tar.zst 使用了未知的压缩,放弃操作 0
统信uosboot区分未挂载导致更新备份失败 0
格之格打印机dp3300系列国产系统uos打印机驱动选择 0
以openkylin为例编译安装内核 0
作者收入月榜
1

prtyaa 收益400.53元

2

zlj141319 收益237.46元

3

哆啦漫漫喵 收益231.42元

4

IT-feng 收益219.71元

5

1843880570 收益214.2元

6

风晓 收益208.24元

7

777 收益173.07元

8

Fhawking 收益106.6元

9

信创来了 收益106.03元

10

克里斯蒂亚诺诺 收益91.08元

请使用微信扫码

添加我为好友,拉您入交流群!

请使用微信扫一扫!