Django REST Framework(DRF)框架之认证Authentication与权限Permission

1,099 阅读6分钟

认证与权限

Django REST Framework (DRF)提供了一系列的认证与权限功能来保护Web API不被未授权用户访问或滥用。

认证(Authentication)

在DRF中,认证是指验证用户身份的过程,如果用户提供的凭据(例如用户名和密码)通过验证,则认为该用户是经过认证的用户。

DRF支持多种认证方式:

SessionAuthentication: 基于django的session机制实现的认证方式,在使用时需要先登录获取session id,然后发送请求时携带该session id

BasicAuthentication: 基于HTTP Basic Authentication协议实现的认证方式,用户需要在请求头中携带base64编码后的用户名和密码

TokenAuthentication: 基于Token的认证方式,用户需要先通过用户名和密码获取一个Token,然后在每次请求时携带该Token进行认证

JSONWebTokenAuthentication: 基于JSON Web Token(JWT)的认证方式,具有无状态、可扩展性等优点

DRF框架默认在rest_framework.settings文件中进行了全局认证方案的设置

DEFAULTS = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
    	# sesssion认证
        'rest_framework.authentication.SessionAuthentication', 
        # 基本认证
        'rest_framework.authentication.BasicAuthentication' 
    )
}

在这里插入图片描述

权限(Permission)

在DRF中,权限是指控制用户对API资源访问的能力。权限控制可以限制用户对于视图API的访问和对于具体数据对象的访问。

1.在执行视图的dispatch()方法前,会先进行视图访问权限的判断

2.在通过get_object()获取具体对象时,会进行对象访问权限的判断

DRF提供了多种预设的权限类:

IsAuthenticated: 仅允许已经认证的用户访问

AllowAny: 允许任何人访问

IsAdminUser: 仅允许管理员用户访问

IsAuthenticatedOrReadOnly: 对于未认证的用户只允许进行读取操作,已认证用户可以执行任意操作

DjangoModelPermissions: 基于Django模型的权限控制,与Django admin中的权限控制类似

DjangoObjectPermissions: 基于Django模型对象的权限控制

也可以自定义权限类,实现更加灵活的权限控制。

DRF框架默认在rest_framework.settings文件中进行了全局权限控制方案的设置

DEFAULTS = {
    'DEFAULT_PERMISSION_CLASSES': (
       'rest_framework.permissions.AllowAny', # 允许所有人
    )
}

认证和权限的使用

创建用户用于验证

使用Django自带用户认证系统,创建一个用户,用于登录验证

import os

from django.test import TestCase

from django.contrib.auth.models import User
if not os.environ.get('DJANGO_SETTINGS_MODULE'):
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meiduo_mall.settings')

import django
django.setup()


class MyTest(TestCase):
    User.objects.create_user(username='admin', password='admin')

配置认证与权限(全局)

可以在DRF项目的settings.py文件中修改DRF框架的全局认证方案与全局权限控制方案

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 基本认证
        'rest_framework.authentication.BasicAuthentication',
        # sesssion认证
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        # 将全局权限控制方案设置为仅允许认证用户访问
        'rest_framework.permissions.IsAuthenticated',
    )
}

认证与权限控制配置好后,访问某URL将出现BasicAuthentication认证框。

在这里插入图片描述 认证成功: 在这里插入图片描述 认证失败: 在这里插入图片描述

视图指定认证与权限(局部)

可以在每个视图中通过设置authentication_classess属性与permission_classes 属性设置视图的认证与权限方案

from rest_framework.authentication import SessionAuthentication, BasicAuthentication

class TestView(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 指定当前视图的认证方案,不使用全局认证方案
    authentication_classess = [BasicAuthentication, SessionAuthentication]
    # 指定当前视图的权限控制方案,不使用全局权限控制方案
    permission_classes = [IsAuthenticated]

配合权限,如果认证失败会有两种可能的返回值:

401 Unauthorized 未认证

403 Permission Denied 权限被禁止

自定义权限

概述

自定义权限控制类,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部方法。

.has_permission(self, request, view) :

判断对使用此权限类的视图是否有访问权限, request表示请求对象,view表示当前视图对象,必须返回一个布尔值,指示请求是否被允许。

.has_object_permission(self, request, view, obj):

判断使用此权限类视图某个数据对象是否有访问权限, request表示请求, view表示当前视图, obj为数据对象

两者区别:

如果要在全局范围内控制某些操作,则使用has_permission()方法。如果要在单个对象级别控制某些操作,则使用has_object_permission()方法。

注意:

如果请求被授予访问权限,方法应该返回True,否则返回False。仅当视图级has_permission检查已通过时,才会调用实例级has_object_permission方法。

创建自定义权限类

创建一个名为MyPermission的自定义权限类。该类继承DRF的BasePermission,并覆盖了其中的has_permission方法与has_object_permission方法。

from rest_framework.permissions import BasePermission


class MyPermission(BasePermission):
    # 判断对使用此权限类的视图是否有访问权限
    def has_permission(self, request, view):
        # 任何用户对使用此权限类的视图都没有访问权限
        return True

    # 判断对使用此权限类视图某个数据对象是否有访问权限
    def has_object_permission(self, request, view, obj):
        # 对id为1,3的数据对象有访问权限
        print(obj)
        if obj.id in (1, 3):
            return True
        return False

使用自定义权限类

class TestView(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 指定当前视图的认证方案,不使用全局认证方案
    # authentication_classess = [BasicAuthentication, SessionAuthentication]
    # 指定当前视图的权限控制方案,不使用全局权限控制方案
    # permission_classes = [IsAuthenticated]
    # 使用自定义的权限控制类
    permission_classes = [MyPermission]

使用TokenAuthentication身份验证

在Django REST Framework中,可以使用TokenAuthentication来进行身份验证。TokenAuthentication是基于令牌的身份验证方式,通过向每个用户分配唯一的令牌来识别用户。当用户发送请求时,他们需要在请求头中包含该令牌以进行身份验证。

配置

在settings.py文件中配置

在INSTALLED_APPS中添加rest_framework.authtoken应用程序

INSTALLED_APPS = [
    'rest_framework.authtoken',
]

在REST_FRAMEWORK中配置认证与权限类

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        # 设置使用JSON渲染器
        'rest_framework.renderers.JSONRenderer',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 使用TokenAuthentication认证
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        # 使用IsAuthenticated权限类确保已登录用户才能访问API
        'rest_framework.permissions.IsAuthenticated',
    ],
}

执行数据库迁移

1.在项目根目录下执行命令来生成迁移文件

python manage.py makemigrations

2.运行命令来应用最新的数据库迁移

python manage.py migrate

3.如果曾经手动删除authtoken_token表,请将该表重新创建。通过运行命令来创建该表

python manage.py migrate rest_framework.authtoken

注意: 在进行数据库迁移之前,请务必备份数据库以避免数据丢失。

创建一个认证令牌

先创建一个john用户对象,然后获取john用户对象,使用该对象创建一个Token对象。最后就可以使用这个Token来进行API请求的认证。

# 设置Django运行所依赖的环境变量
import os

from django.test import TestCase

if not os.environ.get('DJANGO_SETTINGS_MODULE'):
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'drf_demo.settings')

# 让Django进行一次初始化
import django

django.setup()

from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User


class MyTest(TestCase):
    # User.objects.create(username='john', password='password')

    user = User.objects.get(username='john')
    token = Token.objects.create(user=user)
    print(token)

创建视图

创建一个MyView视图,然后添加身份验证

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

class MyView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response({'username': request.user.username})

配置路由

urlpatterns = [
    re_path(r'^test/$', views.MyView.as_view(), name='test'),
]

测试

发送请求,Django REST Framework将检查请求头中是否包含有效的令牌,如果是,则允许该请求。 在这里插入图片描述 无效的令牌,拦截。

{
	"detail": "Authentication credentials were not provided."
}