DRF: 权限认证

Django REST Framework(DRF)的权限认证是用于保护 API 的重要机制,它允许开发者控制谁可以访问 API 资源。DRF 提供了多种权限类,可以灵活地根据用户的身份、角色、请求数据等进行授权判断。

1. 认证与权限的区别

在 DRF 中,认证(Authentication)权限(Permissions) 是两个不同的概念:

  • 认证:验证用户的身份,通常是通过检查请求中是否包含有效的令牌、Session、Cookie 或其他凭据来完成。
  • 权限:在认证用户身份后,决定该用户是否有权访问特定的资源。

2. DRF 的权限类

DRF 提供了多种内置的权限类,开发者可以选择合适的权限类来控制访问,也可以自定义权限类。

2.1. 内置权限类

DRF 内置了以下几种常见的权限类:

  • IsAuthenticated: 功能:仅允许已认证的用户访问该视图。 使用场景:适用于需要用户登录后才能访问的 API。 from rest_framework.permissions import IsAuthenticated class MyView(APIView): permission_classes = [IsAuthenticated] def get(self, request): # 认证通过,处理请求 return Response({'message': 'Hello, authenticated user!'})
  • IsAdminUser: 功能:仅允许管理员用户访问该视图(is_staff=True)。 使用场景:适用于管理员权限控制,如管理后台的 API。 from rest_framework.permissions import IsAdminUser class AdminView(APIView): permission_classes = [IsAdminUser] def get(self, request): # 仅管理员用户可以访问 return Response({'message': 'Hello, admin user!'})
  • IsAuthenticatedOrReadOnly: 功能:允许已认证用户执行所有操作,但未认证用户只能进行只读操作(如 GET)。 使用场景:适用于公共 API,未认证用户可以查看数据,但无法修改。 from rest_framework.permissions import IsAuthenticatedOrReadOnly class BookListView(APIView): permission_classes = [IsAuthenticatedOrReadOnly] def get(self, request): # 所有用户都可以查看 return Response({'books': ['Book 1', 'Book 2']}) def post(self, request): # 仅认证用户可以创建书籍 return Response({'message': 'Book created!'})
  • AllowAny: 功能:允许任何人访问该视图,无论是否认证。 使用场景:适用于开放的 API,比如公共资源或用户注册接口。 from rest_framework.permissions import AllowAny class PublicView(APIView): permission_classes = [AllowAny] def get(self, request): # 所有用户都可以访问 return Response({'message': 'This is a public view!'})
  • DjangoModelPermissions: 功能:基于 Django 的权限系统控制用户权限。此权限类会检查用户是否拥有相应模型的 add、change 或 delete 权限。 使用场景:适用于基于模型权限控制的场景。 from rest_framework.permissions import DjangoModelPermissions class MyModelView(APIView): permission_classes = [DjangoModelPermissions] def get(self, request): # 基于 Django 模型权限检查,只有拥有相关权限的用户才能访问 return Response({'message': 'User has model permissions!'})

2.2. 自定义权限类

如果内置的权限类不能满足需求,开发者可以创建自定义的权限类。自定义权限类需要继承 BasePermission 类,并实现 has_permission()has_object_permission() 方法。

  • has_permission():判断请求是否有权限访问该视图,通常用于视图级别的权限判断。
  • has_object_permission():判断用户是否有权限访问特定的对象,通常用于对象级别的权限判断。

示例:自定义权限类

假设我们要实现一个权限类,只有当用户请求的对象的 owner 是当前用户时,才允许访问。

from rest_framework.permissions import BasePermission
from .models import Post

class IsOwner(BasePermission):
    """
    只有对象的拥有者才允许访问该对象
    """

    def has_permission(self, request, view):
        # 这里可以进行全局权限的判断,如认证状态、是否有访问该视图的权限等
        return request.user and request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        # 仅允许对象的所有者进行访问
        return obj.owner == request.user

然后,在视图中使用:

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Post
from .permissions import IsOwner

class PostDetailView(APIView):
    permission_classes = [IsOwner]

    def get(self, request, pk):
        post = Post.objects.get(pk=pk)
        self.check_object_permissions(request, post)  # 检查对象级别的权限
        return Response({'title': post.title, 'content': post.content})

3. 权限类的工作流程

当用户请求一个 API 时,DRF 会依次执行以下步骤来判断是否允许访问:

  1. 认证:首先 DRF 会使用认证类来验证用户的身份。
  2. 权限检查:如果认证成功,DRF 会依次调用所有指定的权限类(permission_classes)来检查该用户是否有权限访问该资源。只有当所有权限类的判断都通过时,才允许访问视图。

4. 组合权限

你可以为视图设置多个权限类,DRF 会按顺序执行所有权限类的检查,只有所有权限检查都通过时,用户才能访问该视图。

from rest_framework.permissions import IsAuthenticated, IsAdminUser

class AdminOrOwnerView(APIView):
    permission_classes = [IsAuthenticated, IsAdminUser]

    def get(self, request):
        # 只有已认证的管理员用户可以访问
        return Response({'message': 'Hello, admin!'})

5. 权限类与视图的结合

权限类可以与视图或视图集(ViewSet)结合使用。在视图中,你可以通过 permission_classes 来指定一个或多个权限类。

class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    permission_classes = [IsAuthenticated]

在上面的代码中,MyViewSet 会根据 IsAuthenticated 权限类来确保只有认证的用户才能访问该视图集。

6. 总结

DRF 的权限认证是控制 API 访问的关键机制,它通过认证用户身份并根据权限类判断用户是否有访问某个资源的权限。通过灵活使用 DRF 提供的内置权限类、组合权限类,以及自定义权限类,你可以构建出非常精细的权限控制机制,保证 API 资源的安全性和合规性。


文章标签:

评论(0)