DRF 权限控制 #

一、权限概述 #

1.1 什么是权限 #

权限控制确定已认证用户是否有权执行特定操作,解决"你能做什么"的问题。

text
请求流程
┌─────────────────┐
│   认证成功      │
│  request.user   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   权限检查      │
│  你能做什么?   │
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌───────┐ ┌───────┐
│ 允许  │ │ 拒绝  │
└───────┘ └───────┘

1.2 权限检查时机 #

检查点 说明
视图级别 检查用户是否有权访问视图
对象级别 检查用户是否有权访问特定对象

1.3 内置权限类 #

权限类 说明
AllowAny 允许所有用户
IsAuthenticated 仅认证用户
IsAdminUser 仅管理员
IsAuthenticatedOrReadOnly 认证用户可写,其他只读

二、内置权限 #

2.1 AllowAny #

允许所有用户访问:

python
from rest_framework.permissions import AllowAny

class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [AllowAny]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

2.2 IsAuthenticated #

仅认证用户可访问:

python
from rest_framework.permissions import IsAuthenticated

class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

2.3 IsAdminUser #

仅管理员可访问:

python
from rest_framework.permissions import IsAdminUser

class UserViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAdminUser]
    queryset = User.objects.all()
    serializer_class = UserSerializer

2.4 IsAuthenticatedOrReadOnly #

认证用户可写,匿名用户只读:

python
from rest_framework.permissions import IsAuthenticatedOrReadOnly

class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

三、自定义权限 #

3.1 创建自定义权限类 #

python
from rest_framework import permissions

class IsOwner(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.owner == request.user

3.2 使用自定义权限 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, IsOwner]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

3.3 视图级别权限 #

python
class IsAdminOrReadOnly(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.method in permissions.SAFE_METHODS:
            return True
        return request.user and request.user.is_staff

3.4 对象级别权限 #

python
class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.author == request.user

四、权限组合 #

4.1 AND组合 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, IsOwner]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

4.2 OR组合 #

python
from rest_framework.permissions import BasePermission, IsAuthenticated

class IsAdminOrOwner(BasePermission):
    def has_object_permission(self, request, view, obj):
        return request.user.is_staff or obj.author == request.user

class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, IsAdminOrOwner]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

4.3 复杂权限逻辑 #

python
class CanEditArticle(BasePermission):
    def has_permission(self, request, view):
        return request.user.is_authenticated
    
    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        
        if request.user.is_staff:
            return True
        
        if obj.author == request.user:
            return True
        
        if obj.editors.filter(id=request.user.id).exists():
            return True
        
        return False

五、动态权限 #

5.1 基于动作的权限 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    def get_permissions(self):
        if self.action == 'create':
            return [IsAuthenticated()]
        if self.action in ['update', 'partial_update', 'destroy']:
            return [IsAuthenticated(), IsOwner()]
        return [AllowAny()]

5.2 基于HTTP方法的权限 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    def get_permissions(self):
        if self.request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
            return [IsAuthenticated(), IsOwner()]
        return [AllowAny()]

5.3 基于用户角色的权限 #

python
class RoleBasedPermission(BasePermission):
    def has_permission(self, request, view):
        required_role = getattr(view, 'required_role', None)
        if not required_role:
            return True
        
        user_role = getattr(request.user, 'role', None)
        return user_role == required_role

class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, RoleBasedPermission]
    required_role = 'editor'
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

六、对象级权限 #

6.1 基本实现 #

python
class IsOwner(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.author == request.user

class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, IsOwner]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

6.2 多条件对象权限 #

python
class IsArticleEditable(BasePermission):
    message = '您没有权限编辑此文章'
    
    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True
        
        if obj.author != request.user:
            return False
        
        if obj.status == 'archived':
            self.message = '已归档的文章不能编辑'
            return False
        
        return True

6.3 检查对象权限 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    def update(self, request, *args, **kwargs):
        article = self.get_object()
        self.check_object_permissions(request, article)
        return super().update(request, *args, **kwargs)

七、权限配置 #

7.1 全局配置 #

python
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

7.2 视图级别配置 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

7.3 函数视图配置 #

python
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def article_list(request):
    return Response({'user': request.user.username})

7.4 action级别配置 #

python
from rest_framework.decorators import action
from rest_framework.permissions import IsAdminUser

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=False, permission_classes=[IsAdminUser])
    def all(self, request):
        articles = self.get_queryset()
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)

八、权限最佳实践 #

8.1 常用权限组合 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    def get_permissions(self):
        if self.action == 'list':
            return [AllowAny()]
        elif self.action == 'create':
            return [IsAuthenticated()]
        elif self.action in ['update', 'partial_update', 'destroy']:
            return [IsAuthenticated(), IsOwner()]
        return [AllowAny()]

8.2 权限消息 #

python
class IsPremiumUser(BasePermission):
    message = '此功能仅限高级会员使用'
    
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.is_premium

8.3 权限缓存 #

python
class CachedPermission(BasePermission):
    _cache = {}
    
    def has_permission(self, request, view):
        user_id = request.user.id
        if user_id not in self._cache:
            self._cache[user_id] = self._check_permission(request)
        return self._cache[user_id]
    
    def _check_permission(self, request):
        return request.user.is_staff

九、实际应用示例 #

9.1 博客系统权限 #

python
class IsAuthorOrReadOnly(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return obj.author == request.user

class IsPublishedOrAuthor(BasePermission):
    def has_object_permission(self, request, view, obj):
        if obj.is_published:
            return True
        return obj.author == request.user

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    def get_permissions(self):
        if self.action == 'list':
            return [AllowAny()]
        elif self.action == 'retrieve':
            return [IsPublishedOrAuthor()]
        elif self.action in ['update', 'partial_update', 'destroy']:
            return [IsAuthenticated(), IsAuthorOrReadOnly()]
        return [IsAuthenticated()]

9.2 组织权限 #

python
class IsOrganizationMember(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.organization.members.filter(id=request.user.id).exists()

class ProjectViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, IsOrganizationMember]
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

十、总结 #

本章学习了DRF权限控制:

  • 内置权限:AllowAny、IsAuthenticated等
  • 自定义权限:创建自定义权限类
  • 权限组合:AND和OR逻辑
  • 动态权限:基于动作和方法的权限
  • 对象级权限:细粒度访问控制

让我们继续学习JWT认证!

最后更新:2026-03-28