djiango DRF的使用
- 一 、初始 DRF序列化
- 环境安装
- 环境配置
- 数据模型定义
- 定义DRF序列化模型对象
- 二 、DRF请求和响应
- 请求对象(Request objects)
- 响应对象(Response objects)
- 状态码(Status codes)
- 包装(wrapping)API视图
- 组合在一起(灵活的FBV视图)
- 路由设置
- 测试
- 给我们的网址添加可选的格式后缀(json、api)
- 三 、DRF类视图使用进化史
- 基于APIView
- GenericAPIView
- 视图配置:
- 路由配置:
- 基于mixins类
- 视图配置:
- 路由配置
- mixins + GenericAPIView组合封装
- 视图配置
- 路由配置
- 修改序列化器字段属性
- ModelViewSet类视图
- 路由配置
- 视图配置
- ReadOnlyModelViewSet
- 导入和继承
- 提供的默认操作方法
- 序列化器(Serializer)的使用
- URL配置
- 四、DRF认证与权限
- 一、Django REST Framework (DRF) 认证配置
- 1. 基本认证(Basic Authentication)
- 2.Session 认证(Session Authentication)
- 获取 Session Cookie(模拟登录过程,因为基于 Session 认证需要有效的 Session)
- 3.Token 认证(Token Authentication)
- 4.JWT 认证(JSON Web Token Authentication)
- 生成 JWT 令牌
- 利用token进行访问book书籍信息
- 二、DRF 权限配置
- 基于用户认证的权限(IsAuthenticated)
- 基于用户角色的权限(如 IsAdminUser 等)
- 自定义权限类
一 、初始 DRF序列化
环境安装
pip install djangorestframework
环境配置
INSTALLED_APPS = ['rest_framework',
]
数据模型定义
from django.db import modelsclass Book(models.Model):title = models.CharField(max_length=200)author = models.CharField(max_length=100)publication_year = models.IntegerField()
定义DRF序列化模型对象
在books app应用下创建一个serializers序列化对象文件
from rest_framework import serializers
from .models import Bookclass BookSerializer(serializers.ModelSerializer):class Meta:model = Bookfields = ['title', 'author', 'publication_year']
二 、DRF请求和响应
从现在开始,我们将真正开始接触REST框架的核心。 我们来介绍几个基本的构建模块。
请求对象(Request objects)
REST框架引入了一个扩展了常规HttpRequest的Request对象,并提供了更灵活的请求解析。Request对象的核心功能是request.data属性,它与request.POST类似,但对于使用Web API更为有用
request.POST # 只处理表单数据 只适用于'POST'方法
request.data # 处理任意数据 适用于'POST','PUT'和'PATCH'方法
响应对象(Response objects)
REST框架还引入了一个Response对象,这是一种获取未渲染(unrendered)内容的TemplateResponse类型,并使用内容协商来确定返回给客户端的正确内容类型。
return Response(data) # 渲染成客户端请求的内容类型。
状态码(Status codes)
在你的视图(views)中使用纯数字的HTTP 状态码并不总是那么容易被理解。而且如果错误代码出错,很容易被忽略。REST框架为status模块中的每个状态代码(如HTTP_400_BAD_REQUEST)提供更明确的标识符。使用它们来代替纯数字的HTTP状态码是个很好的方法。
from rest_framework import status
包装(wrapping)API视图
REST框架提供了两个可用于编写API视图的包装器(wrappers)。
- 用于基于函数视图的@api_view装饰器。
- 用于基于类视图的APIView类。
这些包装器提供了一些功能,例如确保你在视图中接收到Request实例,并将上下文添加到Response,以便可以执行内容协商。
包装器还提供了诸如在适当时候返回405 Method Not Allowed响应,并处理在使用格式错误的输入来访问request.data时发生的任何ParseError异常。
组合在一起(灵活的FBV视图)
好的,我们开始使用这些新的组件来写几个视图。
创建一个DrfApiView.py新视图文件,开始重构我们的视图。
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer@api_view(['GET', 'POST'])
def BookList(request):"""列出所有的book信息,或者创建一个新的book信息。"""if request.method == 'GET':# 声明查询对象book = Book.objects.all()# 声明序列化器 # 设置many=True,这样序列化器就知道要将查询集中的每一个Book对象分别进行序列化,并将它们组合成一个列表返回serializer = BookSerializer(book, many=True)return Response(serializer.data)elif request.method == 'POST':serializer = BookSerializer(data=request.data)# 创建对象前先检查# is_valid()方法首先会检查request.data中的数据是否符合BookSerializer定义的规则if serializer.is_valid():# 保存创建对象的数据到数据库中serializer.save()return Response(serializer.data, status=status.HTTP_201_CREATED)# 不符合定义规则返回报错信息return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
路由设置
from django.urls import path
from .DrfApiView import BookListurlpatterns = [path('books/', BookList, name='book_list'),
]
测试
get
post
改进一下DrfApiView.py,增加一个BooksDetail视图方法,实现get, put,delete 请求
@api_view(['GET', 'PUT', 'DELETE'])
def BooksDetail(request, pk):"""获取,更新或删除一个book实例。"""try:book = Book.objects.get(pk=pk)except book.DoesNotExist:return Response(status=status.HTTP_404_NOT_FOUND)if request.method == 'GET':# 注意这里根据pk返回单个book实例就不用加many=True了serializer = BookSerializer(book)return Response(serializer.data)# 更新book为pk的实例elif request.method == 'PUT':serializer = BookSerializer(book, data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)elif request.method == 'DELETE':book.delete()return Response(status=status.HTTP_204_NO_CONTENT)
路由配置:
from django.urls import path
from .DrfApiView import BookList, BooksDetailurlpatterns = [path('books/', BookList, name='book_list'),path('books_detail/<int:pk>/', BooksDetail, name='BooksDetail'),
]
测试:
给我们的网址添加可选的格式后缀(json、api)
为了充分利用我们的响应不再与单一内容类型连接,我们可以为API路径添加对格式后缀的支持。使用格式后缀给我们明确指定了给定格式的URL,这意味着我们的API将能够处理诸如http://example.com/api/items/4.json之类的URL。
像下面这样在这两个视图中添加一个format关键字参数。
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer@api_view(['GET', 'POST'])
def book_list(request, format=None):"""列出所有的book信息,或者创建一个新的book信息。"""if request.method == 'GET':book = Book.objects.all()serializer = BookSerializer(book, many=True)return Response(serializer.data)elif request.method == 'POST':serializer = BookSerializer(data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data, status=status.HTTP_201_CREATED)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)@api_view(['GET', 'PUT', 'DELETE'])
def book_detail(request, pk, format=None):pass
现在更新urls.py文件,给现有的URL后面添加一组format_suffix_patterns。
from django.urls import path, re_path
from rest_framework.urlpatterns import format_suffix_patterns
from .DrfApiView import book_list, book_detail
'''
re_path则用于需要使用正则表达式来定义复杂路由的场景,
'''
urlpatterns = [re_path(r'^books/$', book_list),re_path(r'^books/(?P<pk>[0-9]+)$', book_detail),
]urlpatterns = format_suffix_patterns(urlpatterns)
测试:
三 、DRF类视图使用进化史
Django drf 基于类的视图(CBV)
我们也可以使用基于类的视图编写我们的API视图,而不是基于函数的视图
基于APIView
books下新建一个DrfCbvView.py视图文件(基于APIView)
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer
from django.http import Http404
class BookList(APIView):"""列出所有的book或者创建一个新的book。"""def get(self, request, format=None):book = Book.objects.all()serializer = BookSerializer(book, many=True)return Response(serializer.data)def post(self, request, format=None):serializer = BookSerializer(data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data, status=status.HTTP_201_CREATED)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)class BookDetail(APIView):"""检索,更新或删除一个Book。"""def get_object(self, pk):try:return Book.objects.get(pk=pk)except Book.DoesNotExist:raise Http404def get(self, request, pk, format=None):book = self.get_object(pk)serializer = BookSerializer(book)return Response(serializer.data)def put(self, request, pk, format=None):book = self.get_object(pk)serializer = BookSerializer(book, data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)def delete(self, request, pk, format=None):book = self.get_object(pk)book.delete()return Response(status=status.HTTP_204_NO_CONTENT)
urls.py
from django.urls import path, re_path
from .DrfCbvView import BookList, BookDetail
from rest_framework.urlpatterns import format_suffix_patterns
'''
re_path则用于需要使用正则表达式来定义复杂路由的场景,
'''
urlpatterns = [path('book/', BookList.as_view(), name='book-list'),re_path(r'book_detail/(?P<pk>\d+)/$', BookDetail.as_view(), name='book-detail')
]urlpatterns = format_suffix_patterns(urlpatterns)
GenericAPIView
GenericAPIView 继承自 APIView,它在 APIView 的基础上进一步抽象和封装了一些与数据库模型操作、序列化等相关的通用行为。
可以在子类中重写这个方法来动态返回查询集。这在需要根据不同条件(如用户权限、请求参数等)返回不同查询集时非常有用。默认情况下,它返回在类属性中定义的 queryset
。
get_serializer_class() 获取序列化的类
get_serializer() 获取序列化器对象
get_queryset() 获取查询集结果
get_object() 获取视图单一资源对象
视图配置:
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer
class BookList(GenericAPIView):"""列出所有的book或者创建一个新的book。"""# 先定义查询对象集数据和序列化器queryset = Book.objects.all()serializer_class = BookSerializerdef get(self, request, *args, **kwargs):serializer = self.get_serializer(self.get_queryset(), many=True)return Response(serializer.data)def post(self, request, *args, **kwargs):serializer = self.get_serializer(data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data, status=status.HTTP_201_CREATED)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)class BookDetail(GenericAPIView):"""检索,更新或删除一个Book示例。"""# 先定义查询对象集数据和序列化器queryset = Book.objects.all()serializer_class = BookSerializerdef get(self, request, pk, *args, **kwargs):book = self.get_object()serializer = self.get_serializer(book)return Response(serializer.data)def put(self, request, pk, *args, **kwargs):book = self.get_object()serializer = self.get_serializer(book, data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)def delete(self, request, pk, *args, **kwargs):book = self.get_object()book.delete()return Response(status=status.HTTP_204_NO_CONTENT)
路由配置:
from django.urls import path
from .DrfCbvView import BookList, BookDetail
'''
re_path则用于需要使用正则表达式来定义复杂路由的场景,
'''
urlpatterns = [path('book/', BookList.as_view(), name='book-list'),path('book_detail/<int:pk>/', BookDetail.as_view(), name='book-detail')
]
基于mixins类
(GenericAPIView进一步封装)
视图配置:
from rest_framework.generics import GenericAPIView
from .models import Book
from rest_framework import mixins
from .serializers import BookSerializerclass SnippetList(mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerdef get(self, request, *args, **kwargs):return self.list(request, *args, **kwargs)def post(self, request, *args, **kwargs):return self.create(request, *args, **kwargs)class SnippetDetail(mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerdef get(self, request, *args, **kwargs):return self.retrieve(request, *args, **kwargs)def put(self, request, *args, **kwargs):return self.update(request, *args, **kwargs)def delete(self, request, *args, **kwargs):return self.destroy(request, *args, **kwargs)
路由配置
from django.urls import path
from .DrfCbvView import BookList, BookDetail
'''
re_path则用于需要使用正则表达式来定义复杂路由的场景,
'''
urlpatterns = [path('book/', BookList.as_view(), name='book-list'),path('book_detail/<int:pk>/', BookDetail.as_view(), name='book-detail')
]
mixins + GenericAPIView组合封装
使用:在自定义类视图方法中,导入相应类方法参数后,只需要2步,定义查询对象和序列化器就行了。
from rest_framework.generics import …
ListCreateAPIView类(查看所有、创建对象的方法)
RetrieveUpdateAPIView类(全更新、部分更新对象的方法)
RetrieveDestroyAPIView类(删除、搜索单条数据的对象方法)
RetrieveUpdateDestroyAPIView类(全更新、部分更新、删除、搜索单条数据对象方法)
UpdateAPIView(更新对象方法)
DestroyAPIView(删除对象方法)
RetrieveAPIView (搜索查看单条数据对象方法)
ListAPIView(查看对象数据方法)
CreateAPIView(创建对象数据方法)
视图配置
from rest_framework import generics
from .models import Book
from .serializers import BookSerializerclass BookView(generics.ListCreateAPIView):queryset = Book.objects.all()serializer_class = BookSerializerclass BookDetailView(generics.RetrieveUpdateDestroyAPIView):queryset = Book.objects.all()serializer_class = BookSerializer
路由配置
from django.urls import path
from .DrfCbvView import BookView, BookDetailView
'''
re_path则用于需要使用正则表达式来定义复杂路由的场景,
'''
urlpatterns = [path('BookView/', BookView.as_view(), name='book-list'),path('BookDetailView/<int:pk>/', BookDetailView.as_view(), name='book-detail')
]
修改序列化器字段属性
修改下实现能部分更新字段信息, 实际项目中前端也会判断
from rest_framework import serializers
from .models import Bookclass BookSerializer(serializers.ModelSerializer):title = serializers.CharField(max_length=200)author = serializers.CharField(max_length=100)# 将允许为空的字段设置为required=False(如果业务逻辑允许这些字段为空)publication_year = serializers.IntegerField(required=False)class Meta:model = Bookfields = ['title', 'author', 'publication_year']
ModelViewSet类视图
GenericViewSet + mixins 的结合
作用: 它把所有方法(增删改查查)全部放在了一个类下
使用: 1. 在自定义类视图方法中,导入ModelViewSet类方法参数后,只需要2步,定义查询对象和序列化器就行了。
- url路由改为注册添加方式
from rest_framework.viewsets import ModelViewSet
路由配置
from django.urls import path,include
from rest_framework.routers import DefaultRouter
from .DrfCbvView import BookViewrouter = DefaultRouter()
'''
这里的books是 URL 前缀,BookView是视图集类。
DefaultRouter会根据视图集类中定义的方法(如list、retrieve、create、update、destroy)自动生成对应的 URL 模式。
这样,Router会自动为BookModelViewSet生成以下 URL 模式:books/:get获取Book所有对象的列表。books/<pk>/:get方法,用于获取单个Book对象(其中<pk>是Book对象的主键)。books/:用于创建新的Book对象(实际上是通过POST请求发送数据到books/这个 URL 来实现创建功能)。books/<pk>/:用于更新指定的Book对象(通过PUT或PATCH请求)。books/<pk>/:用于删除指定的Book对象(通过DELETE请求)。
'''
router.register('books', BookView)
urlpatterns = [# path('', include(router.urls)),
]
# 这里单独+配置方便上面添加其它路由
urlpatterns += router.urls
视图配置
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializerclass BookView(ModelViewSet):queryset = Book.objects.all()serializer_class = BookSerializer
测试:
ReadOnlyModelViewSet
它仅支持list和retrive这两个可读的操作
以下是关于它的详细使用方法:
导入和继承
首先,在你的视图文件(通常是views.py
)中需要导入ReadOnlyModelViewSet
,并且让你的视图类继承自它。假设你有一个Book
模型,示例如下:
from rest_framework.viewsets import ReadOnlyModelViewSet
from.models import Book
from.serializers import BookSerializerclass BookReadOnlyViewSet(ReadOnlyModelViewSet):queryset = Book.objects.all()serializer_class = BookSerializer
在这个示例中,BookReadOnlyViewSet
继承了ReadOnlyModelViewSet
。
通过设置queryset
属性指定了视图集操作的数据集(这里是所有的Book
对象),serializer_class
属性则指定了用于序列化和反序列化数据的序列化器类。
提供的默认操作方法
ReadOnlyModelViewSet
提供了两种默认的只读操作方法:
list
方法:用于获取模型对象的列表。- 当客户端发送一个
GET
请求到视图集对应的列表URL(例如/books/
)时,list
方法会被调用。 - 它会从
queryset
中获取所有的对象,通过serializer_class
将这些对象序列化为合适的数据格式(如JSON),然后将序列化后的数据作为响应返回给客户端。
- 当客户端发送一个
retrieve
方法:用于获取单个模型对象的详细信息。- 当客户端发送一个
GET
请求到视图集对应的详情URL(例如/books/1/
,其中1
是Book
对象的主键)时,retrieve
方法会被调用。 - 它会根据请求中的主键从
queryset
中获取对应的单个对象,再使用serializer_class
进行序列化,并将结果返回给客户端。
- 当客户端发送一个
序列化器(Serializer)的使用
- 定义序列化器:
from rest_framework import serializersclass BookSerializer(serializers.ModelSerializer):class Meta:model = Bookfields = '__all__'
- 序列化器用于将模型实例转换为可以在网络上传输的数据格式(如JSON),以及将接收到的格式数据转换为模型实例(在可写视图集中使用)。在上述示例中,`BookSerializer`可能是一个简单的基于模型的序列化器,例如:
- 这个序列化器定义了它基于`Book`模型,并且通过`fields = '__all__'`表示要序列化模型的所有字段。你也可以根据需要指定具体的字段列表,如`fields = ['title', 'author', 'publication_date']`。
- 在视图集中使用序列化器:
- 在
BookReadOnlyViewSet
中,通过serializer_class = BookSerializer
指定了使用这个序列化器。当执行list
或retrieve
操作时,视图集会自动调用这个序列化器的相应方法进行数据的序列化。
- 在
URL配置
- 自动配置(推荐):
from rest_framework.routers import DefaultRouter
from .views import BookReadOnlyViewSetrouter = DefaultRouter()
router.register('books', BookReadOnlyViewSet)urlpatterns = [# path('', include(router.urls)),
]
# 这里单独+配置方便上面添加其它路由
urlpatterns += router.urls
- 可以使用Django REST framework中的`Router`来自动配置`BookReadOnlyViewSet`的URL。
- 在`urls.py`文件中,示例如下:
- 这样,`Router`会自动为`BookReadOnlyViewSet`生成以下URL模式:* `books/`:对应`list`方法,用于获取`Book`对象的列表。* `books/<pk>/`:对应`retrieve`方法,用于获取单个`Book`对象(其中`<pk>`是`Book`对象的主键)。
- 手动配置:
from django.urls import path
from.views import BookReadOnlyViewSeturlpatterns = [path('books/', BookReadOnlyViewSet.as_view({'get': 'list'}), name='book - list'),path('books/<int:pk>/', BookReadOnlyViewSet.as_view({'get':'retrieve'}), name='book - retrieve'),
]
- 如果不想使用`Router`自动配置,也可以手动配置URL。在`urls.py`中,示例如下:
- 这里的`as_view`方法的参数是一个字典,键是`GET`请求方法,值是视图集类中的方法名(`list`或`retrieve`)。
四、DRF认证与权限
认证与权限一定要配合使用才行(2个都要配置)
认证与权限都分全局配置(settins.py中)与某个视图中局部配置两种配置方案
一个是drf框架视图全局生效,一个是drf框架局部视图生效
注意: 在每个视图或视图集单独指定认证与权限方式,会覆盖全局配置。
一、Django REST Framework (DRF) 认证配置
1. 基本认证(Basic Authentication)
- 配置步骤:
- 全局配置,在
settings.py
文件中,将rest_framework
的DEFAULT_AUTHENTICATION_CLASSES
配置项添加基本认证类。 - 例如:
- 全局配置,在
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.BasicAuthentication',]
}
- 或者局部配置在视图类或视图集中,可以通过设置
authentication_classes
属性来指定使用的认证方式。 - 例如:
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializer
# 认证
from rest_framework.authentication import BasicAuthentication
# 权限
from rest_framework.permissions import IsAuthenticatedclass BookView(ModelViewSet):authentication_classes = [BasicAuthentication]permission_classes = [IsAuthenticated]queryset = Book.objects.all()serializer_class = BookSerializer
- 原理:基本认证是通过在请求头中发送
Authorization
字段,格式为Basic <base64编码的用户名:密码>
来进行认证, 基本认证通常只适用于测试。 - DRF 的基本认证类会解析这个请求头,验证用户的用户名和密码。
- 测试访问 : http://127.0.0.1:8000/books/ 会弹出用户密码登录验证框
2.Session 认证(Session Authentication)
https://www.cnblogs.com/zhangqigao/p/12800935.html
- 安装必要的应用:
- 确保
django.contrib.sessions
在INSTALLED_APPS
中已经添加。 - 这是 Django 内置的用于处理会话(Session)的应用,默认情况下在新建的 Django 项目中已经添加。
- 确保
- 配置中间件:
- 确认
django.contrib.sessions.middleware.SessionMiddleware
在MIDDLEWARE
列表中。这个中间件用于处理会话相关的操作,如创建、读取和更新会话。它的位置很重要,通常应该放在合适的位置,以确保在请求处理过程中能够正确地处理会话。例如,它应该放在AuthenticationMiddleware
之前,这样在进行用户认证时可以利用会话信息。
- 确认
- 在
REST_FRAMEWORK **全局配置**
配置项中,可以设置DEFAULT_AUTHENTICATION_CLASSES
来指定使用 Session 认证作为默认的认证方式。例如:
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication',]}
- 视图级别配置
- 对于基于函数的视图:
- 可以使用
@authentication_classes
装饰器来指定认证方式。例如:
- 可以使用
- 对于基于函数的视图:
from rest_framework.decorators import authentication_classes
from rest_framework.authentication import SessionAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response@authentication_classes([SessionAuthentication])
def my_view(request):# 这里可以访问通过Session认证后的用户信息user = request.userreturn Response({"message": "Hello, {}".format(user.username)})
- 对于基于类的视图和视图集:
- 在视图类或视图集中设置
authentication_classes
属性。例如:
- 在视图类或视图集中设置
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Responseclass MyView(APIView):authentication_classes = [SessionAuthentication]permission_classes = [IsAuthenticated]def get(self, request):user = request.userreturn Response({"message": "Hello, {}".format(user.username)})
. url配置:
urlpatterns = [path('booksession/', MyView.as_view()),
]
获取 Session Cookie(模拟登录过程,因为基于 Session 认证需要有效的 Session)
- 方式一:通过浏览器登录获取(如果有对应的登录页面)
- 首先,在浏览器中访问你的 Django 项目的登录页面(假设存在),输入合法的用户名和密码进行登录。登录成功后,浏览器会自动存储后端返回的包含 Session ID 的 Cookie。
- 然后,你可以通过浏览器的开发者工具(通常是按
F12
键调出),在 “Application”(不同浏览器可能名称稍有不同)选项卡中,找到 “Cookies” 部分,查看当前域名下存储的 Cookie,找到对应的 Session ID 相关的 Cookie 及其值(例如sessionid=xxxxxxxxxxxx
)。 - 我们在访问 admin 后台登录,输入用户名密码的时候,后台会自动生成 session
- 登录后,就可以直接访问视图相关信息了:
- postman测试:
- 方式二:使用curl模拟登录请求(如果有对应的 API 登录端点)
- 假设你的项目有一个登录的 API 端点,比如
/api/login/
,并且接受username
和password
作为表单数据来进行登录验证,那么可以使用以下curl
命令模拟登录(以下命令中的用户名和密码需要替换为实际有效的值):
- 假设你的项目有一个登录的 API 端点,比如
curl -X POST -d "username=your_username" -d "password=your_password" http://127.0.0.1:8000/api/login/
- 执行这个命令后,如果登录成功,服务器会在响应中设置包含 Session ID 的 Cookie。不过要注意,
curl
默认情况下不会自动存储和发送 Cookie,所以还需要进一步配置curl
来处理 Cookie.
CSRF Token 与 Session 的关系
- 在 Django 中,`CSRFToken`(跨站请求伪造令牌)和`Session`是相关但不同的概念。`CSRFToken`主要用于防止跨站请求伪造攻击。当一个用户通过浏览器访问一个网站并成功建立会话(`Session`)后,Django 会为每个表单或需要进行状态改变的请求(如 POST、PUT、DELETE 等)生成一个`CSRFToken`,并将其存储在用户的`Session`中(服务器端),同时在页面的表单或通过其他方式(如在 HTML 的`meta`标签中)发送给客户端。
- 当客户端发送一个需要进行状态改变的请求时,需要在请求中包含这个`CSRFToken`,服务器会验证请求中的`CSRFToken`是否与存储在`Session`中的一致,以此来防止恶意网站发起的跨站请求伪造攻击。
3.Token 认证(Token Authentication)
- 配置步骤:
- 首先,需要在项目中使用
rest_framework.authtoken
应用。 - 在
INSTALLED_APPS
中添加'rest_framework.authtoken'
。
- 首先,需要在项目中使用
INSTALLED_APPS = [# add drf authentication'rest_framework.authtoken',
]
- 运行 `python manage.py migrate` 来创建 `Token` 模型对应的数据库表。
- 然后在 `settings.py` 中配置 `DEFAULT_AUTHENTICATION_CLASSES`
- 与 DEFAULT_AUTHENTICATION_CLASSES
# 全局配置
REST_FRAMEWORK = {# 认证'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.TokenAuthentication',),# 权限'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',]
}
局部视图生效的配置方式:
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializer
# 认证
from rest_framework.authentication import TokenAuthentication
# 权限
from rest_framework.permissions import IsAdminUserclass BookView(ModelViewSet):authentication_classes = [TokenAuthentication]permission_classes = [IsAdminUser]queryset = Book.objects.all()serializer_class = BookSerializer
- 为用户创建
Token
,可以通过代码创建(例如在用户注册或登录成功后): - python manage.py shell
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import Useruser = User.objects.get(username='admin')
token, created = Token.objects.get_or_create(user=user)
或者直接在管理后台页面中为用户创建 Token
:
- 客户端在请求时,需要在请求头中添加
Authorization: Token <token_value>
。 - 原理:用户登录后会获得一个唯一的
Token
,之后每次请求都带上这个Token
,DRF 的TokenAuthentication
类会验证Token
的有效性来确定用户身份。
命令行工具curl 可用于测试基于Token认证的API,例如:
curl -X GET http://127.0.0.1:8000/books/ -H 'Authorization: Token 6aec32eaee0e11c9d708e703fb3f1cbf7a5b20c9'
postman测试:
注意: 如果你在生产环境下使用TokenAuthentication认证,你必须确保你的API仅在https可用。
4.JWT 认证(JSON Web Token Authentication)
- 配置步骤(使用第三方库如djangorestframework-simplejwt):
- 安装
djangorestframework - simplejwt
,例如通过pip install djangorestframework - simplejwt
。
- 安装
- 详细参考配置文档:
https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html
- 在
settings.py
中配置全局视图的认证与权限:
# 必配置的jwt模块
INSTALLED_APPS = ['rest_framework','rest_framework_simplejwt',
]
# 全局方式配置认证与权限
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': [# jwt认证方式'rest_framework_simplejwt.authentication.JWTAuthentication',]# 权限'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',]
}
- 配置局部认证与权限的方式:
from rest_framework.authentication import JWTAuthentication
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializer
from rest_framework.permissions import IsAuthenticatedclass BookView(ModelViewSet):# 局部视图中单独配置认证与权限的验证方式authentication_classes = [JWTAuthentication]permission_classes = [IsAuthenticated]queryset = Book.objects.all()serializer_class = BookSerializer
注意:最终局部视图中认证与权限还是会覆盖全局的配置
生成 JWT 令牌
可以通过登录视图例如:
from rest_framework_simplejwt.views import (TokenObtainPairView,TokenRefreshView,
)urlpatterns = [path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
获取token:
刷新访问token:
- 或者使用库提供的方法来实现。例如:
from rest_framework_simplejwt.tokens import RefreshTokendef get_tokens_for_user(user):refresh = RefreshToken.for_user(user)return {'refresh': str(refresh),'access': str(refresh.access_token),}
利用token进行访问book书籍信息
二、DRF 权限配置
. AllowAny 允许所有用户
. IsAuthenticated 仅通过认证的用户
. IsAuthenticatedOrReadOnly 认证的用户可以完全操作,否则只能get读取
. IsAdminUser 仅管理员用户
基于用户认证的权限(IsAuthenticated)
- 配置步骤:
- 全局配置:
- 通过
DEFAULT_PERMISSION_CLASSES
配置全局权限类
REST_FRAMEWORK = {# 认证'DEFAULT_AUTHENTICATION_CLASSES': (# 'rest_framework.authentication.TokenAuthentication','rest_framework.authentication.BasicAuthentication',# 'rest_framework.authentication.SessionAuthentication',# 'rest_framework.permissions.IsAuthenticated',),# 权限'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',]
}
- 或者局部视图类或视图集中设置 `permission_classes` 属性。
- 例如:
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializer
# 认证
from rest_framework.authentication import BasicAuthentication
# 权限
from rest_framework.permissions import IsAuthenticatedclass BookView(ModelViewSet):authentication_classes = [BasicAuthentication]permission_classes = [IsAuthenticated]queryset = Book.objects.all()serializer_class = BookSerializer
- 原理:只有通过认证的用户(即认证成功的用户)才能访问该视图。
- 继承自django原生的后台管理用户
- 当未认证用户尝试访问时,会收到
401 Unauthorized
或403 Forbidden
错误(取决于具体的认证和权限配置细节)。
基于用户角色的权限(如 IsAdminUser 等)
- **配置步骤**:* DRF 提供了一些基于用户角色的权限类,例如 `IsAdminUser`。* 在视图中配置如下:
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializer
# 认证
from rest_framework.authentication import BasicAuthentication
# 权限
from rest_framework.permissions import IsAdminUserclass BookView(ModelViewSet):authentication_classes = [BasicAuthentication]permission_classes = [IsAdminUser]queryset = Book.objects.all()serializer_class = BookSerializer
- 原理:
IsAdminUser
权限类会检查用户是否是管理员(通常是通过检查user.is_staff
属性),只有管理员用户才能访问配置了该权限类的视图。
自定义权限类
- 配置步骤:
- 首先,创建自定义权限类。例如,创建一个只允许特定 IP 地址访问的权限类:
from rest_framework.permissions import BasePermissionclass IPWhitelistPermission(BasePermission):def has_permission(self, request, view):ip_whitelist = ['127.0.0.1', '192.168.0.1']return request.META.get('REMOTE_ADDR') in ip_whitelist
- 然后在视图中使用这个自定义权限类:
from rest_framework.views import APIViewclass MyIPRestrictedView(APIView):permission_classes = [IPWhitelistPermission]def get(self, request):return Response({"message": "Only whitelisted IPs can access"})
- 原理:自定义权限类需要继承自
BasePermission
,并实现has_permission
方法(用于判断是否有权限访问视图)或has_object_permission
方法(用于判断是否有权限访问对象)。在上述例子中,通过检查请求的 IP 地址是否在白名单中来决定用户是否有访问权限。