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": "认证失败"
}
相关推荐
冬天vs不冷3 小时前
Java基础(十四):枚举类详解
android·java·python
这里有鱼汤3 小时前
如何用Python找到股票的支撑位和压力位?——成交量剖面
后端·python
AI量化投资实验室3 小时前
今日策略:年化436%,回撤7%,夏普比5.28, deap因子挖掘重构,附python代码
开发语言·python·重构
一粒马豆3 小时前
excel表格通过前端fetch上传至后端flask处理流程示例
前端·python·flask·excel·h5·js·fetch
Q_Q5110082853 小时前
python+django/flask哈利波特书影音互动科普网站
spring boot·python·django·flask·node.js·php
小欣加油4 小时前
python123 机器学习基础练习2
人工智能·python·深度学习·机器学习
DIY机器人工房4 小时前
关于如何让 Vosk 正确识别中文音频,核心是 使用 Vosk 中文模型 + 确保中文音频格式符合要求
开发语言·python
百锦再4 小时前
一文掌握Flask:从基础使用到高级应用
后端·python·django·flask·virtualenv·scikit-learn·pygame
Q_Q19632884754 小时前
python+springboot+uniapp基于微信小程序的巴马旅居养老系统 旅游养老小程序
spring boot·python·小程序·django·flask·uni-app·node.js