DRF的版本认证权限

Posted yidashi110

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DRF的版本认证权限相关的知识,希望对你有一定的参考价值。

DRF的版本

版本控制是做什么用的, 我们为什么要用

首先我们要知道我们的版本是干嘛用的呢~~大家都知道我们开发项目是有多个版本的~~

当我们项目越来越更新~版本就越来越多~~我们不可能新的版本出了~以前旧的版本就不进行维护了~~~

那我们就需要对版本进行控制~~这个DRF也给我们提供了一些封装好的版本控制方法~~

版本控制怎么用

之前我们学视图的时候知道APIView,也知道APIView返回View中的view函数,然后调用的dispatch方法~

技术分享图片

执行self.initial方法之前是各种赋值,包括request的重新封装赋值,下面是路由的分发,那我们看下这个方法都做了什么~~

技术分享图片

我们可以看到,我们的version版本信息赋值给了 request.version  版本控制方案赋值给了 request.versioning_scheme~~

其实这个版本控制方案~就是我们配置的版本控制的类~~

也就是说,APIView通过这个方法初始化自己提供的组件~~

我们接下来看看框架提供了哪些版本的控制方法~~在rest_framework.versioning里~~

技术分享图片

详细用法

a. 基于url的get传参方式

如:/users?version=v1

技术分享图片
REST_FRAMEWORK = {
    DEFAULT_VERSION: v1,            # 默认版本
    ALLOWED_VERSIONS: [v1, v2],   # 允许的版本
    VERSION_PARAM: version          # URL中获取值的key
}
settings.py
技术分享图片
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^test/, TestView.as_view(),name=test),
]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning


class TestView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):

        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)

        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse(test, request=request)
        print(reverse_url)

        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)

views.py
views.py

b. 基于url的正则方式

如:/v1/users/

技术分享图片
REST_FRAMEWORK = {
    DEFAULT_VERSION: v1,            # 默认版本
    ALLOWED_VERSIONS: [v1, v2],   # 允许的版本
    VERSION_PARAM: version          # URL中获取值的key
}
settings.py
技术分享图片
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^(?P<version>[v1|v2]+)/test/, TestView.as_view(), name=test),
]

urls.py
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning


class TestView(APIView):
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)

        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse(test, request=request)
        print(reverse_url)

        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)

views.py
views.py

c. 基于 accept 请求头方式

如:Accept: application/json; version=1.0

技术分享图片
REST_FRAMEWORK = {
    DEFAULT_VERSION: v1,            # 默认版本
    ALLOWED_VERSIONS: [v1, v2],   # 允许的版本
    VERSION_PARAM: version          # URL中获取值的key
}
settings.py
技术分享图片
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^test/, TestView.as_view(), name=test),
]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning


class TestView(APIView):
    versioning_class = AcceptHeaderVersioning

    def get(self, request, *args, **kwargs):
        # 获取版本 HTTP_ACCEPT头
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse(test, request=request)
        print(reverse_url)

        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

d. 基于主机名方法

如:v1.example.com

技术分享图片
ALLOWED_HOSTS = [*]
REST_FRAMEWORK = {
    DEFAULT_VERSION: v1,  # 默认版本
    ALLOWED_VERSIONS: [v1, v2],  # 允许的版本
    VERSION_PARAM: version  # URL中获取值的key
}
settings.py
技术分享图片
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^test/, TestView.as_view(), name=test),
]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioning


class TestView(APIView):
    versioning_class = HostNameVersioning

    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse(test, request=request)
        print(reverse_url)

        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

e. 基于django路由系统的namespace

如:example.com/v1/users/

技术分享图片
REST_FRAMEWORK = {
    DEFAULT_VERSION: v1,  # 默认版本
    ALLOWED_VERSIONS: [v1, v2],  # 允许的版本
    VERSION_PARAM: version  # URL中获取值的key
}
settings.py
技术分享图片
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^v1/, ([
                      url(rtest/, TestView.as_view(), name=test),
                  ], None, v1)),
    url(r^v2/, ([
                      url(rtest/, TestView.as_view(), name=test),
                  ], None, v2)),

]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning


class TestView(APIView):
    versioning_class = NamespaceVersioning

    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse(test, request=request)
        print(reverse_url)

        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

f. 全局使用

技术分享图片
REST_FRAMEWORK = {
    DEFAULT_VERSIONING_CLASS:"rest_framework.versioning.URLPathVersioning",
    DEFAULT_VERSION: v1,
    ALLOWED_VERSIONS: [v1, v2],
    VERSION_PARAM: version 
}
settings.py

DRF的认证

上面讲版本的时候我们知道~在dispatch方法里~执行了initial方法~~那里初始化了我们的版本~~

如果我们细心我们能看到~版本的下面其实就是我们的认证,权限,频率组件了~~

我们先看看我们的认证组件~~

技术分享图片

我们进去我们的认证看下~~

技术分享图片

我们这个权限组件返回的是request.user,那我们这里的request是新的还是旧的呢~~

我们的initial是在我们request重新赋值之后的~所以这里的request是新的~也就是Request类实例对象~~

那这个user一定是一个静态方法~我们进去看看~~

技术分享图片

技术分享图片

我没在这里反复的截图跳转页面~~大家可以尝试着自己去找~~要耐心~~细心~~

我们通过上面基本可以知道我们的认证类一定要实现的方法~~以及返回值类型~~以及配置的参数authentication_classes~

认证的详细用法

a. 用户url传入的token认证

技术分享图片
from django.conf.urls import url, include
from web.viewsimport TestView

urlpatterns = [
    url(r^test/, TestView.as_view()),
]
urls.py
技术分享图片
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    sfsfss123kuf3j123,
    asijnfowerkkf9812,
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER()
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        val = request.query_params.get(token)
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")

        return (登录用户, 用户token)

    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.
        """
        # 验证失败时,返回的响应头WWW-Authenticate对应的值
        pass


class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

b. 请求头认证

技术分享图片
from django.conf.urls import url, include
from web.viewsimport TestView

urlpatterns = [
    url(r^test/, TestView.as_view()),
]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    sfsfss123kuf3j123,
    asijnfowerkkf9812,
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER()
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        import base64
        auth = request.META.get(HTTP_AUTHORIZATION, b‘‘)
        if auth:
            auth = auth.encode(utf-8)
        auth = auth.split()
        if not auth or auth[0].lower() != bbasic:
            raise exceptions.AuthenticationFailed(验证失败)
        if len(auth) != 2:
            raise exceptions.AuthenticationFailed(验证失败)
        username, part, password = base64.b64decode(auth[1]).decode(utf-8).partition(:)
        if username == alex and password == 123:
            return (登录用户, 用户token)
        else:
            raise exceptions.AuthenticationFailed(用户名或密码错误)

    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.
        """
        return Basic realm=api


class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

c. 多个认证规则

技术分享图片
from django.conf.urls import url, include
from web.views.s2_auth import TestView

urlpatterns = [
    url(r^test/, TestView.as_view()),
]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    sfsfss123kuf3j123,
    asijnfowerkkf9812,
]


class Test1Authentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                else:
                    self.user = None

                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        import base64
        auth = request.META.get(HTTP_AUTHORIZATION, b‘‘)
        if auth:
            auth = auth.encode(utf-8)
        else:
            return None
        print(auth,xxxx)
        auth = auth.split()
        if not auth or auth[0].lower() != bbasic:
            raise exceptions.AuthenticationFailed(验证失败)
        if len(auth) != 2:
            raise exceptions.AuthenticationFailed(验证失败)
        username, part, password = base64.b64decode(auth[1]).decode(utf-8).partition(:)
        if username == alex and password == 123:
            return (登录用户, 用户token)
        else:
            raise exceptions.AuthenticationFailed(用户名或密码错误)

    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.
        """
        # return ‘Basic realm=api‘
        pass

class Test2Authentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        val = request.query_params.get(token)
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")

        return (登录用户, 用户token)

    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


class TestView(APIView):
    authentication_classes = [Test1Authentication, Test2Authentication]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)

views.py#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    sfsfss123kuf3j123,
    asijnfowerkkf9812,
]


class Test1Authentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                else:
                    self.user = None

                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        import base64
        auth = request.META.get(HTTP_AUTHORIZATION, b‘‘)
        if auth:
            auth = auth.encode(utf-8)
        else:
            return None
        print(auth,xxxx)
        auth = auth.split()
        if not auth or auth[0].lower() != bbasic:
            raise exceptions.AuthenticationFailed(验证失败)
        if len(auth) != 2:
            raise exceptions.AuthenticationFailed(验证失败)
        username, part, password = base64.b64decode(auth[1]).decode(utf-8).partition(:)
        if username == alex and password == 123:
            return (登录用户, 用户token)
        else:
            raise exceptions.AuthenticationFailed(用户名或密码错误)

    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.
        """
        # return ‘Basic realm=api‘
        pass

class Test2Authentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        val = request.query_params.get(token)
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")

        return (登录用户, 用户token)

    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


class TestView(APIView):
    authentication_classes = [Test1Authentication, Test2Authentication]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

d. 认证和权限

技术分享图片
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^test/, TestView.as_view()),
]
urls.py
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission

from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    sfsfss123kuf3j123,
    asijnfowerkkf9812,
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 
        :return: 
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
        """
        val = request.query_params.get(token)
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")

        return (登录用户, 用户token)

    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


class TestPermission(BasePermission):
    message = "权限验证失败"

    def has_permission(self, request, view):
        """
        判断是否有权限访问当前请求
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :return: True有权限;False无权限
        """
        if request.user == "管理员":
            return True

    # GenericAPIView中get_object时调用
    def has_object_permission(self, request, view, obj):
        """
        视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :param obj: 
        :return: True有权限;False无权限
        """
        if request.user == "管理员":
            return True


class TestView(APIView):
    # 认证的动作是由request.user触发
    authentication_classes = [TestAuthentication, ]

    # 权限
    # 循环执行所有的权限
    permission_classes = [TestPermission, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

e. 全局使用

技术分享图片
REST_FRAMEWORK = {
    UNAUTHENTICATED_USER: None,
    UNAUTHENTICATED_TOKEN: None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "web.utils.TestAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "web.utils.TestPermission",
    ],
}
settings.py
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r^test/, TestView.as_view()),
]
技术分享图片
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

class TestView(APIView):

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response(GET请求,响应内容)

    def post(self, request, *args, **kwargs):
        return Response(POST请求,响应内容)

    def put(self, request, *args, **kwargs):
        return Response(PUT请求,响应内容)
views.py

 

以上是关于DRF的版本认证权限的主要内容,如果未能解决你的问题,请参考以下文章

DRF 版本认证权限限制解析器和渲染器

DRF 版本认证权限限制解析器和渲染器

DRF的版本认证权限

DRF的版本认证权限

drf认证节流权限版本

DRF之版本控制认证和权限组件