关于drf框架中的用户认证

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

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

javascript 复制代码
from rest_framework.authentication import BaseAuthentication

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

kotlin 复制代码
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

kotlin 复制代码
# 判断请求头是否有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包

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

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

python 复制代码
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行我们可以知道他调用了父类的方法,进去查看一下

python 复制代码
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方法有很多都有,我们要看看是谁调用的这个方法,这里涉及面向对象的知识,不懂的可以去回顾一下)

python 复制代码
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行进行了一个定义方法,我们进去查看一下

python 复制代码
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行我们来看一下

python 复制代码
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

python 复制代码
@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
python 复制代码
@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()这个方法是什么呢,我们进去看看

python 复制代码
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来获取数据了

相关推荐
罗政2 小时前
[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离
vue.js·spring boot·后端
拾光师3 小时前
spring获取当前request
java·后端·spring
Java小白笔记5 小时前
关于使用Mybatis-Plus 自动填充功能失效问题
spring boot·后端·mybatis
JOJO___6 小时前
Spring IoC 配置类 总结
java·后端·spring·java-ee
白总Server7 小时前
MySQL在大数据场景应用
大数据·开发语言·数据库·后端·mysql·golang·php
Lingbug8 小时前
.Net日志组件之NLog的使用和配置
后端·c#·.net·.netcore
计算机学姐8 小时前
基于SpringBoot+Vue的篮球馆会员信息管理系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
好兄弟给我起把狙9 小时前
[Golang] Select
开发语言·后端·golang
程序员大金9 小时前
基于SpringBoot+Vue+MySQL的智能物流管理系统
java·javascript·vue.js·spring boot·后端·mysql·mybatis
ac-er888810 小时前
在Flask中处理后台任务
后端·python·flask