drf中认证组件的初步使用

在drf中使用认证组件,要两个步骤。一是组件的编写,二是组件的应用。

组件应用

组件的应用,组件的应用分为两种模式,一种是全局应用,一种是局部应用。

  • 全局应用就是写到drf的全局配置文件中,也就是django的setting.py文件的drf配置内容中。这种情况下,视图文件中所有的视图类都会默认带上全局配置中的认证组件。
  • 局部应用是写到具体的视图类中,也就是写到views.py中。这种情况下,就只有视图类中配置了认证组件的视图类才会去认证。

先说组件应用方式的原因,是因为不同的应用,组件编写的位置是不同的。如果是全局应用,那么这个组件就一定不能 编写到视图文件views.py中,而如果只是局部应用,那么组件的编写就是可以放到视图文件views.py中的。

在drf中一般来说,认证组件应该是局部应用和全局应用都会一起用,所有这个组件就都不要写到视图文件view.py中去。所以下面以组件不写到视图文件中,写到项目根目录下的utils/auth.py目录下为例来分别介绍。

示例目录结构如下:

powershell 复制代码
drf_study/$
├── apps/
│   └── api/
│       ├── migrations/
│       ├── __init__.py
│       ├── admin.py
│       ├── apps.py
│       ├── models.py
│       ├── tests.py
│       └── views.py
├── drf_study/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── utils/
│   └── auth.py
├── db.sqlite3
├── manage.py
└── requirements.txt

同时我们在apps/api/views.py中定义三个视图类:

  1. login,可以不认证直接访问。
  2. user,必须要认证才可以访问。
  3. order,必须要认证才可以访问。
python 复制代码
# apps/api/views.py
from rest_framework.response import Response  
from rest_framework.views import APIView  
  
  
class LoginView(APIView):  
    def get(self, request):  
        context = {  
            'content':'login View',  
        }  
        return Response(context)  
  
  
class UserView(APIView):  
    def get(self, request):  
        context = {  
            'content':'user View',  
        }  
        return Response(context)  
  
  
class OrderView(APIView):  
    def get(self, request):  
        context = {  
            'content':'order View',  
        }  
        return Response(context)
        
# drf_study/urls.py
from django.urls import path  
from apps.api import views  
  
urlpatterns = [  
    path('login/', views.LoginView.as_view(), name='login'),  
    path('user/', views.UserView.as_view(), name='user'),  
    path('order/', views.OrderView.as_view(), name='order'),  
]

全局应用

全局应用就是在setting.py文件中加入如下的配置项,主要配置项实际上就是组件的编写的路径,在上面的例子中,当然是utils/auth/MyAuthentication。键就是固定的DEFAULT_AUTHENTICATION_CLASSES,这个键是存在于APIView这个类中的一个变量。

python 复制代码
REST_FRAMEWORK = {  
    'UNAUTHENTICATED_USER': None,  
    # '键':['组件的路径,要具体到组件这个类']  
    'DEFAULT_AUTHENTICATION_CLASSES':['utils.auth.MyAuthentication']  
}

配置了全局,访问下面三个都必须要认证,也就是说必须要带上token才可以。

python 复制代码
path('login/', views.LoginView.as_view(), name='login'),  
path('user/', views.UserView.as_view(), name='user'),  
path('order/', views.OrderView.as_view(), name='order'),  

如果要让login不需要认证,就只需要在LoginView中加上一个局部的认证配置项authentication_classes,这个配置项的值是一个列表,为空就不需要进行认证。

python 复制代码
class LoginView(APIView):  
    authentication_classes = []  # 这个就是在全局应用下的局部配置
  
    def get(self, request):  
        context = {  
            'content':'login View',  
        }  
        return Response(context)

如果全局配置和局部配置都有值,那么drf会只取局部配置中的值,全局的就不再生效了。

这里再来说一下,为什么全局应用的情况下,为什么组件不可以写到views.py中。

如果把认证组件写到views.py中,在全局应用下,drf在加载setting.py的时候会去加载组件的路径,也就是views.py中的那个组件,而在加载views.py的中的视图类的时候,又会去加载在views.py中导入的APIView,而在APIView中又会去读取api_setting中全局配置中的值,而drf中api_setting就是指的setting.py中的drf的配置,然后又会去views.py中找这个组件,导致死循环了,所有全局应用下,认证组件不可以写到views.py中。

局部应用

局部应用可以直接写到views.py视图中,只需要把utils/auth.py中的内容在views.py中写进去就可以了,就是把组件MyAuthentication写到views.py文件中。然后再在后续的每个视图类中,分别去应用主键就可以了。

python 复制代码
from rest_framework.response import Response  
from rest_framework.views import APIView  
from rest_framework.authentication import BaseAuthentication  
from rest_framework.exceptions import AuthenticationFailed  
  
  
class MyAuthentication(BaseAuthentication):  
    def authenticate(self, request):  
        token = request.query_params.get('token')  
  
        # 认证成功,假设获取到token就算认证成功  
        if token:  
            return ('stark', token)  
  
        # 认证失败返回  
        context = {  
            'code':2000,  
            'error':'认证失败'  
        }  
        raise AuthenticationFailed(context)  
  
class LoginView(APIView):  
    authentication_classes = []  # 列表中没有组件,就不会去做认证操作
  
    def get(self, request):  
        context = {  
            'content':'login View',  
        }  
        return Response(context)  
  
  
class UserView(APIView):  
    authentication_classes = [MyAuthentication,]    # 局部应用中就是在每个视图类中增加这个配置,注意不加引号了  
    def get(self, request):  
        context = {  
            'content':'user View',  
        }  
        return Response(context)  
  
  
class OrderView(APIView):  
    authentication_classes = [MyAuthentication,]    # 局部应用中就是在每个视图类中增加这个配置,注意不加引号了  
    def get(self, request):  
        context = {  
            'content':'order View',  
        }  
        return Response(context)

组件编写

组件的编写就是编写一个类,在上面应用中的就是一个认证组件类。

python 复制代码
from rest_framework.authentication import BaseAuthentication  
from rest_framework.exceptions import AuthenticationFailed    # 抛出异常需要用到这个

class MyAuthentication(BaseAuthentication):  
    def authenticate(self, request):  
        token = request.query_params.get('token')  
  
        # 认证成功,假设获取到token就算认证成功  
        if token:  
            return ('stark', token)  
  
        # 认证失败返回  
        context = {  
            'code':2000,  
            'error':'认证失败'  
        }  
        raise AuthenticationFailed(context)  

这个认证组件类要继承自rest_framework.authentication.BaseAuthentication,同时里面必须要定义一个函数authenticate,这个函数中具体来定义认证流程。上面的例子中,假设的就是只要在链接中获取到了token的值就算认证成功。

BaseAuthenticationauthenticate函数中,就是做认证具体的地方,在这个函数中,一般主要做三件事情:

  1. 读取请求传递的token
  2. 校验token的合法性
  3. 返回结果,而返回结果一般来说有三种情况
    • 返回元组,认证成功,必须返回包含两个参数的元组。这两个参数就是(user,auth),返回了之后,后续的用这个组件的视图类中就可以使用request.userrequest.auth来获取这两个值。注意这里的request是drf中的Request对象中的request
    • 抛出异常,认证失败,返回错误信息。抛出异常就需要用到AuthenticationFailed类。
    • 返回None。如果认证组件有多个类,比如[类1,类2,类3],那么在类1authenticate认证返回None的时候,就会去类2中执行authenticate函数,如果还是返回None,就去类3中执行authenticate,如果都返回None,那么就返回匿名用户,如果中间有一个认证成功或者认证失败,就不往下去执行了。这个None有点像表达"我不知道该认证成功,还是认证失败,所以返回None"。

这里面获取token的这句本质上也是调用的django的request.GET.get('token'),django中的query_params就是_request.GET。这里的query_params后跟的.get实际上字典自带的一个内置方法get()

小知识点:

  • 在python中dict['key']dict.get('key')的区别主要在于get()方法可以配置一个默认值,即使从字典中没有取到值,也不会报错。get()方法默认返回None,dict.get(key, default=None)

源码示意流程

认证组件的加载与authenticate函数的执行

状态码的问题

是指响应头中的状态码,比如最上面如果访问path('user/', views.UserView.as_view(), name='user'),的时候没有认证通过,返回的是 HTTP 403 Forbidden

python 复制代码
**HTTP 403 Forbidden**
**Allow:** GET, HEAD, OPTIONS
**Content-Type:** application/json
**Vary:** Accept

{
    "code": "2000",
    "error": "认证失败"
}

但是实际上在源码中的流程大概是这样:

所以在认证类中也需要加上authenticate_header方法。

python 复制代码
from rest_framework.authentication import BaseAuthentication  
from rest_framework.exceptions import AuthenticationFailed  
  
  
class MyAuthentication(BaseAuthentication):  
    def authenticate(self, request):  
        token = request.query_params.get('token')  
  
        # 认证成功,假设获取到token就算认证成功  
        if token:  
            return ('stark', token)  
  
        # 认证失败返回  
        context = {  
            'code':2000,  
            'error':'认证失败'  
        }  
        raise AuthenticationFailed(context)  
  
    def authenticate_header(self, request):  
        return 'API'  #随便返回什么,然后就可以正常响应到响应头中

加了authenticate_header方法后的响应:

python 复制代码
**HTTP 401 Unauthorized**  # 状态码变成正常的401
**Allow:** GET, HEAD, OPTIONS
**Content-Type:** application/json
**Vary:** Accept
**WWW-Authenticate:** API  # 这就是authenticate_header方法返回的内容

{
    "code": "2000",
    "error": "认证失败"
}
相关推荐
Dxy123931021617 分钟前
Python基于BERT的上下文纠错详解
开发语言·python·bert
SiYuanFeng1 小时前
Colab复现 NanoChat:从 Tokenizer(CPU)、Base Train(CPU) 到 SFT(GPU) 的完整踩坑实录
python·colab
炸炸鱼.2 小时前
Python 操作 MySQL 数据库
android·数据库·python·adb
_深海凉_3 小时前
LeetCode热题100-颜色分类
python·算法·leetcode
AC赳赳老秦3 小时前
OpenClaw email技能:批量发送邮件、自动回复,高效处理工作邮件
运维·人工智能·python·django·自动化·deepseek·openclaw
zhaoshuzhaoshu4 小时前
Python 语法之数据结构详细解析
python
AI问答工程师4 小时前
Meta Muse Spark 的"思维压缩"到底是什么?我用 Python 复现了核心思路(附代码)
人工智能·python
zfan5205 小时前
python对Excel数据处理(1)
python·excel·pandas
小饕5 小时前
我从零搭建 RAG 学到的 10 件事
python
老歌老听老掉牙5 小时前
PyQt5+Qt Designer实战:可视化设计智能参数配置界面,告别手动布局时代!
python·qt