Skip to content

Django Rest Framework (DRF)

介绍

Django REST framework (DRF) 是一个功能强大且灵活的工具包,用于在 Django 中构建 Web API。

你可以把它理解为 Django 的一个“超级英雄”扩展。Django 本身最擅长的是构建具有前端模板的“全栈”网站(即后端+前端页面)。而 DRF 的职责是专门让 Django 专注于扮演强大、安全的“后端 API 服务器”角色,为各种客户端(如 Web 前端、移动App、其他服务等)提供数据接口。

它的核心作用是:将你的 Django 数据模型(Models)快速、轻松、标准化地转换为 RESTful 风格的 API 接口。

主要特点和核心功能

DRF 之所以如此流行,是因为它封装了构建 API 的许多通用、繁琐且容易出错的部分,提供了“开箱即用”的解决方案:

  1. 序列化与反序列化

    这是 DRF 最核心的功能,也是 API 数据交互的基础。

    • 序列化:将 Django 的模型(Model)实例或复杂的查询集(QuerySet)转换为易于传输的数据格式,如 JSON 或 XML。这通常在读取数据(GET请求)时使用。

    • 反序列化:将客户端发送的 JSON 等数据格式验证并解析成 Python 数据类型,进而可以创建或更新模型实例。这通常在创建(POST)、更新(PUT/PATCH)数据时使用。

  2. 类视图和视图集

    • APIView: 基于类的视图,提供了比 Django 原生 View 更适用于 API 的请求/响应循环。
    • GenericAPIViewMixin 类:提供了与常见操作(如列表、创建、检索、更新、删除)绑定的通用行为,让你写极少的代码就能完成 CRUD 接口。
    • ViewSetModelViewSet:最高级别的封装,一个 ModelViewSet 类可以自动为你提供一个模型的所有 CRUD 端点(List, Create, Retrieve, Update, Delete, Partial Update)。
  3. 路由系统

    DRF 的 Router 类可以自动将 ViewSet 映射到 URL 配置上。你只需要注册一个 ViewSet,路由器就会自动生成标准的 RESTful URL,例如:

    • /api/books/ -> 对应列表(GET)和创建(POST)

    • /api/books/1/ -> 对应详情(GET)、更新(PUT)、删除(DELETE)

    这避免了手动编写大量重复的 URL 模式。

  4. 认证 (Authentication) 与权限 (Permissions)

    API 的安全性至关重要,DRF 提供了开箱即用的、可插拔的认证和权限系统。

    • 认证:确定用户的身份。DRF 支持多种认证方案:

    • SessionAuthentication(基于 Django 的会话,适合前后端未完全分离的场景)

    • TokenAuthentication(使用令牌,最常见的方式之一)

    • JWTAuthentication(使用 JSON Web Token,更现代和安全的方式)

    • 以及 OAuth 等。

    • 权限:确定已认证的用户有权执行什么操作。

      • IsAuthenticated(是否已登录)

      • IsAdminUser(是否是管理员)

      • IsAuthenticatedOrReadOnly(未登录用户只读)

      • DjangoModelPermissions(基于 Django 的模型权限)

      • 还可以轻松地自定义权限类。

  5. 强大的请求和响应对象

    DRF 扩展了 Django 的 HttpRequest 和 HttpResponse。

    • Request 对象:对原生请求进行了包装,其 request.data 属性可以自动解析 JSON 或其他格式的请求体,比 Django 原生的 request.POST 更通用。

    • Response 对象:它是一个未渲染的模板响应,会根据客户端请求的内容类型(如 JSON)自动将数据渲染为合适的格式。

  6. Browsable API

    这是一个非常受欢迎的开发者友好功能。当你启动开发服务器并访问 DRF 生成的 API 端点时,它会呈现一个美观、可交互的 Web 界面。你可以直接在这个界面上查看 API 结构、进行 GET、POST、PUT 等操作,极大地方便了 API 的测试和调试。

  7. 可定制性

    尽管 DRF 提供了高度封装的组件,但它也极其灵活。几乎所有默认行为都可以通过重写方法、自定义类(如自定义序列化器、权限类、分页类、过滤器等)来进行覆盖和扩展。

序列化与反序列化

这是 DRF 最核心的功能,也是 API 数据交互的基础。

序列化:将 Django 的模型(Model)实例或复杂的查询集(QuerySet)转换为 JSON 或 XML。这通常在读取数据(GET请求)时使用。

反序列化:将客户端发送的 JSON 等数据格式验证并解析成 Python 数据类型,进而可以创建或更新模型实例。这通常在创建(POST)、更新(PUT/PATCH)数据时使用。

  • DRF序列化 DRF序列化器提供了一种可以在视图中直接进行序列化的方式:

    python
    # serializers.py
    class GoodsSerializer(serializers.ModelSerializer):
        # 这里写的字段是想要进行自定义处理的字段
    
        p_price = serializers.SerializerMethodField()
    
        # 自定义序列化方法时,方法名必须是 get_<字段名>
        def get_p_price(self, obj):
            return float(obj.p_price)
    
        # create update方法会在serializer.save()时自动调用
        def create(self, validated_data):
          """
          Create a new "article" instance
          """
        def update(self, validated_data):
          """
          Create a new "article" instance
          """
          return Goods.objects.create(**validated_data)
    
        class Meta:
            model = Goods
            fields = '__all__'
    python
    # views.py
    
    class GoodsMainMenu(View):
        def get(self, request):
            # 获取MainMenu表中的所有行
            main_menu = MainMenu.objects.all()
            # 反序列化: data
            serializer = GoodsSerializer(data=main_menu, many=True)
    
            # 序列化: instance
            result = GoodsSerializer(instance=main_menu, many=True)
            return HttpResponse(result)

类视图和视图集

APIView

APIView 继承自 Django 的 View 类。但是 APIView 做了以下增强:

  • 请求对象: 通过重写 View 的 dispatch() 将Django的 HttpRequest 对象封装为DRF的 Request 对象
  • 响应对象: 使用 DRF 的Response对象,可以将响应轻松渲染成 JSON 等类型。
  • 异常处理: 提供了专门的异常处理,将Django和Python异常自动转换为合适的HTTP响应
  • 认证与权限: 内置了认证、权限、限流等API相关功能的钩子

APIView源码分析:

python
# DRF APIView 的 dispatch 方法简化逻辑
class APIView(View):
    def dispatch(self, request, *args, **kwargs):
        # ...

        # 关键扩展 1: 将 Django 的 HttpRequest 对象转换为 DRF 的 Request 对象
        # DRF 的 'request' 添加了解析器、认证器、渲染器等 DRF 特有功能,
        # 提供更强大的 API(如 .data、.user、.auth 等属性)
        request = self.initialize_request(request, *args, **kwargs)

        # ...

        try:
          # 关键扩展 2:传入DRF request, 执行请求处理前的各种初始化操作
          self.initial(request, *args, **kwargs)
          """
          内部执行(按顺序):
            1.身份认证 (Authentication):确定请求用户身份
            2.权限检查 (Permission):检查用户是否有权限执行该操作
            3.流量限制 (Throttling):检查请求频率是否超过限制
            4.内容协商 (Content Negotiation):确定合适的渲染器
          """

          # 根据 HTTP 方法(GET、POST、PUT 等)分派到对应的处理方法。
          if request.method.lower() in self.http_method_names:
              handler = getattr(self, request.method.lower(),
                                self.http_method_not_allowed)
          else:
              handler = self.http_method_not_allowed

          # 实际执行对应的 HTTP 方法处理函数(如 get(), post() 等)。
          response = handler(request, *args, **kwargs)

        # 关键扩展 3:统一异常处理,捕获所有在处理过程中抛出的异常
        # 将 Django 异常或 Python 内置异常转换为格式化的 API 响应
        # 支持不同类型的异常对应不同的 HTTP 状态码和错误信息格式
        except Exception as exc:
            response = self.handle_exception(exc)


        # 关键扩展 4:形成DRF response
        # 确保所有响应都有正确的 Content-Length 头
        # 基于客户端请求的 Accept 头选择合适的渲染器(JSON、XML、HTML等),并添加正确的 Content-Type 头
        # 添加必要的响应头,如:Content-Type: application/json, text/html 等
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

GenericAPIView

GenericAPIView继承自APIView, 它在APIView基础上进一步抽象了通用模式。它通常不直接使用, 而是与 Mixin 类(如 ListModelMixin, CreateModelMixin)结合使用,快速实现标准的 CRUD(增删改查)操作。相较于APIView它的增强如下:

  • 模型和查询集相关属性

    python
    # 基础模型配置
    queryset = None
    serializer_class = None
    
    # 查询集过滤和筛选
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    filterset_class = None
    filterset_fields = None
    paginator = None
    
    # 查询集查找字段
    lookup_field = 'pk'
    lookup_url_kwarg = None
  • 核心方法扩展

    • get_queryset():获取查询集
    • get_object():获取单个对象
    • get_serializer():获取序列化器
    • filter_queryset:查询集过滤器
    • paginate_queryset():查询集分页
    • get_paginated_response():生成分页响应

示例:

python
# views.py
class UserDetailView(GenericAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    lookup_field = 'username'
    
    def get(self, request, username):
        user = self.get_object()  # 使用扩展的get_object方法
        serializer = self.get_serializer(user)  # 使用扩展的get_serializer
        return Response(serializer.data)
    
    def put(self, request, username):
        user = self.get_object()
        serializer = self.get_serializer(user, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

ListModelMixin - 列表查询

功能:提供列表查询功能(GET 方法查询多个对象)

  • 源码:

    python
    class ListModelMixin:
      def list(self, request, *args, **kwargs):
          queryset = self.filter_queryset(self.get_queryset())
          
          page = self.paginate_queryset(queryset)
          if page is not None:
              serializer = self.get_serializer(page, many=True)
              return self.get_paginated_response(serializer.data)
          
          serializer = self.get_serializer(queryset, many=True)
          return Response(serializer.data)
  • 示例:

    python
    from rest_framework import mixins, generics
    
    class UserListView(mixins.ListModelMixin, generics.GenericAPIView):
        queryset = User.objects.all()
        serializer_class = UserSerializer
        
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)

RetrieveModelMixin - 详情查询

功能:提供单个对象查询功能(GET 方法查询单个对象)

  • 源码:

    python
    class RetrieveModelMixin:
      def retrieve(self, request, *args, **kwargs):
          instance = self.get_object()
          serializer = self.get_serializer(instance)
          return Response(serializer.data)
  • 示例:

    python
    class UserDetailView(mixins.RetrieveModelMixin, generics.GenericAPIView):
      queryset = User.objects.all()
      serializer_class = UserSerializer
      
      def get(self, request, *args, **kwargs):
          return self.retrieve(request, *args, **kwargs)

CreateModelMixin - 创建对象

功能:提供创建新对象功能(POST 方法)

  • 源码:

    python
    class CreateModelMixin:
      def create(self, request, *args, **kwargs):
          serializer = self.get_serializer(data=request.data)
          serializer.is_valid(raise_exception=True)
          self.perform_create(serializer)
          headers = self.get_success_headers(serializer.data)
          return Response(serializer.data, status=201, headers=headers)
  • 示例:

    python
    class UserCreateView(mixins.CreateModelMixin, generics.GenericAPIView):
      queryset = User.objects.all()
      serializer_class = UserSerializer
      
      def post(self, request, *args, **kwargs):
          return self.create(request, *args, **kwargs)

UpdateModelMixin - 更新对象

功能:提供更新对象功能(PUT/PATCH 方法)

  • 源码:

    python
    class UpdateModelMixin:
      def update(self, request, *args, **kwargs):
          partial = kwargs.pop('partial', False)
          instance = self.get_object()
          serializer = self.get_serializer(instance, data=request.data, partial=partial)
          serializer.is_valid(raise_exception=True)
          self.perform_update(serializer)
          return Response(serializer.data)
  • 示例:

    python
    class UserUpdateView(mixins.UpdateModelMixin, generics.GenericAPIView):
      queryset = User.objects.all()
      serializer_class = UserSerializer
      
      def put(self, request, *args, **kwargs):
          return self.update(request, *args, **kwargs)
      
      def patch(self, request, *args, **kwargs):
          return self.update(request, *args, **kwargs, partial=True)

DestroyModelMixin - 删除对象

功能:提供删除对象功能(DELETE 方法)

  • 源码:

    python
    class DestroyModelMixin:
      def destroy(self, request, *args, **kwargs):
          instance = self.get_object()
          self.perform_destroy(instance)
          return Response(status=204)
  • 示例:

    python
    class UserDeleteView(mixins.DestroyModelMixin, generics.GenericAPIView):
      queryset = User.objects.all()
      serializer_class = UserSerializer
      
      def delete(self, request, *args, **kwargs):
          return self.destroy(request, *args, **kwargs)

完整的 CRUD 视图

python
class UserViewSet(mixins.ListModelMixin,
                 mixins.RetrieveModelMixin,
                 mixins.CreateModelMixin,
                 mixins.UpdateModelMixin,
                 mixins.DestroyModelMixin,
                 generics.GenericAPIView):
    
    queryset = User.objects.all()
    serializer_class = UserSerializer
    
    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            return self.retrieve(request, *args, **kwargs)
        return self.list(request, *args, **kwargs)
    
    def post(self, request, *args, **kwargs):
        return self.create(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)

视图集 ViewSetMixin

视图集是一种将多个相关视图的逻辑组合到一个类中的高级抽象。它允许开发者用更少的代码实现完整的 CRUD(Create, Retrieve, Update, Delete)操作。

  • 特点

    • 减少重复代码:将 list、create、retrieve、update、delete 等操作整合到一个类中

    • 自动化路由:配合路由器(Router)自动生成 URL 配置

    • 保持一致性:确保同一资源的不同操作使用相同的查询集和序列化器配置

    • 提高开发效率:用极少的代码快速构建完整的 RESTful API

  • 核心视图集类

    • ViewSet:基础视图集类,可定制自己的视图逻辑

    • GenericViewSet:最常用的基类,提供模型操作工具

    • ModelViewSet:提供完整 CRUD 操作的"全能"视图集

    • ReadOnlyModelViewSet:提供只读操作的视图集

ViewSet

这是最基础的 ViewSet 类,它本身不提供任何默认的 action(如 list, create)。你需要自己定义所有方法。它通常用于实现非常定制化的逻辑。

python
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer

class BasicBookViewSet(viewsets.ViewSet):
    """
    一个基础的 ViewSet,用于演示手动实现各个动作。
    """

    # GET /api/books/
    def list(self, request):
        queryset = Book.objects.all()
        serializer = BookSerializer(queryset, many=True)
        return Response(serializer.data)

    # POST /api/books/
    def create(self, request):
        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)

    # GET /api/books/1/
    def retrieve(self, request, pk=None):
        try:
            book = Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookSerializer(book)
        return Response(serializer.data)

    # PUT /api/books/1/
    def update(self, request, pk=None):
        try:
            book = Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        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)

    # PATCH /api/books/1/
    def partial_update(self, request, pk=None):
        try:
            book = Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookSerializer(book, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # DELETE /api/books/1/
    def destroy(self, request, pk=None):
        try:
            book = Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        book.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    # 自定义 Action: GET /api/books/1/latest/
    # detail=True 表示这是针对单个对象的操作
    @action(detail=True, methods=['get'])
    def latest(self, request, pk=None):
        # 这个例子可能不太符合‘latest’,但展示了如何访问单个对象
        book = self.get_object() # 需要配合 mixin 或自己实现 get_object
        # 假设我们返回这个对象的创建时间信息
        return Response({'id': book.id, 'title': book.title})

    # 自定义 Action: GET /api/books/published_after/?year=2020
    # detail=False 表示这是针对整个集合的操作
    @action(detail=False)
    def published_after(self, request):
        year = request.query_params.get('year', 2000)
        books = Book.objects.filter(published_date__year__gt=year)
        serializer = self.get_serializer(books, many=True)
        return Response(serializer.data)

GenericViewSet

GenericViewSet 继承了 ViewSet,同时也继承了 DRF 的通用视图(GenericAPIView)的行为,比如 get_queryset(), get_serializer() 等方法。它本身仍然不提供默认的 action,但为你提供了构建 action 的通用工具。你通常会结合 mixins 来使用它。

python

from rest_framework import viewsets, mixins

# 通过混合 Mixin 来快速构建 ViewSet
class GenericBookViewSet(mixins.CreateModelMixin,
                         mixins.RetrieveModelMixin,
                         mixins.UpdateModelMixin,
                         mixins.DestroyModelMixin,
                         mixins.ListModelMixin,
                         viewsets.GenericViewSet):
    """
    一个使用 GenericViewSet 和 Mixin 的示例。
    这个 ViewSet 将自动提供 `list`, `create`, `retrieve`, `update`, `partial_update` 和 `destroy` 动作。
    注意:我们混入了哪些 Mixin,就拥有哪些动作。
    """
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 例如,如果我们不想提供 list 功能,只需去掉 ListModelMixin 即可。

ModelViewSet

这是最强大、最常用的 ViewSet。它继承了 GenericViewSet,并一次性包含了所有标准的 mixin 类。如果你需要对一个模型进行完整的 CRUD(增删改查),这是最佳选择。

python
from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer

class BookViewSet(viewsets.ModelViewSet):
    """
    一个完整的 ModelViewSet 示例。
    自动提供所有标准动作: list, create, retrieve, update, partial_update, destroy。
    """
    queryset = Book.objects.all().order_by('-published_date') # 可以在这里定义默认排序
    serializer_class = BookSerializer

    # 你还可以轻松地添加权限、过滤、分页等
    # permission_classes = [IsAuthenticated]
    # filter_backends = [DjangoFilterBackend]
    # filterset_fields = ['author', 'published_date']

ReadOnlyModelViewSet

顾名思义,这个 ViewSet 只提供只读操作。它继承自 GenericViewSet,但只包含 RetrieveModelMixin 和 ListModelMixin。适用于你希望数据对外是只读的场景。

python
from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer

class ReadOnlyBookViewSet(viewsets.ReadOnlyModelViewSet):
    """
    一个只读的 ViewSet。
    自动提供 list 和 retrieve 动作。
    """
    queryset = Book.objects.all()
    serializer_class = BookSerializer

认证与权限

核心概念

首先,必须理解认证和权限是两个不同但紧密相关的概念。

  1. 认证 - 识别“你是谁

    认证的目的是回答一个问题:“这个请求是谁发出的?” 系统需要确认用户的身份。

    • 机制:当请求到达 DRF 视图时,认证类会检查请求中的凭据(如 session cookie、token 等)。

    • 结果:如果认证成功,DRF 会将认证后的用户对象赋值给 request.user;如果失败,request.user 会被设置为 AnonymousUser 的实例;如果没有任何凭据,则会触发 WWW-Authenticate 头,导致 HTTP 401 Unauthorized 响应。

    DRF 内置的常见认证方式:

    • SessionAuthentication: 使用 Django 的会话后端进行认证。适用于前后端不分离或紧密耦合的场景(如 Django 模板渲染)。

    • TokenAuthentication: 简单的基于 Token 的认证。服务器生成一个随机字符串 Token 与用户关联,客户端在请求头中携带此 Token。

    • BasicAuthentication: 使用 HTTP Basic Auth,将用户名和密码进行 Base64 编码后传输。仅建议在 HTTPS 下使用。

  2. 权限 - 决定“你能做什么

    权限的目的是在认证之后,回答另一个问题:“这个被认证的用户是否有权限执行这个操作?”

    • 机制:在所有认证器运行完毕后,权限类会检查 request.user 和 request.auth(认证的附加信息,如 token),根据预定义的规则决定是否允许请求继续。

    • 结果:如果任何权限检查失败,DRF 将返回 HTTP 403 Forbidden 响应。

    DRF 内置的常见权限类:

    • AllowAny: 允许所有请求,无论是否认证。这是默认设置。

    • IsAuthenticated: 只允许已认证的用户访问。

    • IsAdminUser: 只允许 user.is_staffTrue 的用户(管理员)访问。

    • IsAuthenticatedOrReadOnly: 已认证用户可以执行任何操作,未认证用户只能执行安全操作(如 GET、HEAD、OPTIONS)。

    • DjangoModelPermissions: 根据 Django 的标准模型权限(add_模型, change_模型, delete_模型)进行控制。

JWT Token 认证

安装依赖

bash
pip install djangorestframework-simplejwt

项目配置

settings.py中配置:

python
INSTALLED_APPS = [
    ...,
    'rest_framework',
    'rest_framework_simplejwt'
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 配置 DRF 使用 JWT 认证
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}

# JWT 配置
from datetime import timedelta

SIMPLE_JWT = {
    # token 有效期
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),

    # 允许刷新 token
    'ROTATE_REFRESH_TOKENS': False,        # 是否在刷新时返回新的 Refresh Token
    'BLACKLIST_AFTER_ROTATION': False,     # 是否将旧的 Refresh Token 加入黑名单

    # 加密算法
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,  # 使用 Django 的 SECRET_KEY

    # 认证头类型
    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',

    # token 类型声明
    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
}

路由配置

在项目根 url.py 或者某个app的 urls.py 中配置:

python
from django.urls import path
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView,
)

urlpatterns = [
    # ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # 获取令牌(登录)
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # 使用refresh token刷新access token
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),   # 验证access token是否有效
    # ...
]

JWT 的核心端点:Access Token 和 Refresh Token

simplejwt 采用了双 token 机制,这是业界最佳实践,旨在平衡安全性和用户体验。

  • Access Token

    • 用途:用于访问受保护的 API 端点。生命周期较短(如 5-60 分钟)。

    • 获取端点:POST /api/token/,提供 username 和 password,返回 access 和 refresh token。

    • 使用方式:在请求头中 Authorization: Bearer <access_token>

  • Refresh Token

    • 用途:当 Access Token 过期后,用于获取一对新的 Access TokenRefresh Token。生命周期较长(如数天或数周)。

    • 获取新 Token 的端点:POST /api/token/refresh/,提供过期的 refresh token,返回新的 access token(有时也会返回新的 refresh token,取决于配置)。

    • 重要性:用户无需重新登录,体验更好。同时,服务器可以通过使 Refresh Token 失效来强制用户重新登录,增强了安全性。

  • 一个典型的前端交互流程:

    1. 用户登录

      用户提交用户名和密码,前端 POST 到 /api/token/。将返回的 access_tokenrefresh_token 保存起来(例如在 localStoragesessionStorageCookie 中)。

    2. API 请求

      在每次调用需要认证的 API 时,在 HTTP 请求头中加入 Authorization: Bearer <access_token>

    3. 处理 Token 过期

      • 发起 API 请求,如果返回 401 错误,说明 access_token 可能过期了。

      • 前端自动(或提示用户)调用 /api/token/refresh/ 接口,使用 refresh_token 获取新的 access_token

      • 用新的 access_token 重试失败的请求。

    4. 用户退出登录

      前端直接丢弃存储的 token 即可。

JWT认证流程实现

  1. 用户登录获取JWT

    python
    # views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework_simplejwt.tokens import RefreshToken
    from django.contrib.auth import authenticate
    
    class UserLoginAPIView(APIView):
        def post(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            
            # 验证用户凭证
            user = authenticate(username=username, password=password)
            
            if user:
                # 生成 JWT token
                refresh = RefreshToken.for_user(user)
                
                return Response({
                    'access': str(refresh.access_token),
                    'refresh': str(refresh),
                    'user_id': user.id,
                    'username': user.username
                })
            else:
                return Response({'error': 'Invalid credentials'}, status=400)
  2. 客户端存储和发送JWT

    客户端在登录成功后,将 JWT 存储在本地(通常是在 localStorage 或 cookie 中),并在后续请求的 Header 中携带:

    js
    // 前端示例
    // 存储 token
    localStorage.setItem('access_token', response.data.access);
    
    // 在请求头中携带 token
    axios.get('/api/protected-resource/', {
        headers: {
            'Authorization': `Bearer ${localStorage.getItem('access_token')}`
        }
    });
  3. Django 配置JWT认证

    python
    # settings.py
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ),
    }
    
    # JWT 配置
    SIMPLE_JWT = {
        'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
        'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
        'AUTH_HEADER_TYPES': ('Bearer',),
    }
  4. 访问需要认证的视图

    python
    # views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.permissions import IsAuthenticated
    
    class ProtectedView(APIView):
        permission_classes = [IsAuthenticated]
        
        def get(self, request):
            # 此时 request.user 就是认证后的用户对象
            user = request.user
            return Response({
                'message': f'Hello {user.username}',
                'user_id': user.id,
                'email': user.email
            })
  5. url配置

    python
    # urls.py
    from django.urls import path
    from rest_framework_simplejwt.views import TokenRefreshView
    from .views import LoginView, ProtectedView
    
    urlpatterns = [
        path('api/login/', LoginView.as_view(), name='login'),
        path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
        path('api/protected/', ProtectedView.as_view(), name='protected'),
    ]

Browsable API

Released under the MIT License.