跳至内容

认证、权限与限流

DRF 在每次请求的 dispatch() 阶段依次执行认证 → 权限 → 限流三道防线,然后才将请求交给视图方法处理。这三个组件均支持全局配置和视图级局部覆盖,且都可以自定义扩展。

认证(Authentication)

认证负责识别"这个请求是谁发的",并将结果赋值到 request.userrequest.auth。认证失败不会直接拒绝请求(拒绝是权限层的职责),而是将用户标记为匿名用户(AnonymousUser)。

内置认证类

认证类说明
SessionAuthentication基于 Django Session,适合浏览器端(含 CSRF 检查)
BasicAuthenticationHTTP Basic Auth,用户名/密码 Base64 编码,仅适合开发调试
TokenAuthenticationToken 令牌认证,需要 rest_framework.authtoken 应用
RemoteUserAuthentication委托给 Django REMOTE_USER 中间件

全局配置

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ]
}

视图级局部覆盖

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class MyAPIView(APIView):
    authentication_classes = [SessionAuthentication, BasicAuthentication]

自定义认证类

继承 BaseAuthentication 并实现 authenticate() 方法。该方法应返回 (user, auth) 元组,或在认证失败时抛出 AuthenticationFailed,或返回 None(表示本认证类放弃,交给下一个)。

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth.models import User


class TokenHeaderAuthentication(BaseAuthentication):
    """从自定义请求头 X-Token 中读取 token 并查找用户"""

    def authenticate(self, request):
        token = request.META.get("HTTP_X_TOKEN")
        if not token:
            return None  # 未携带 token,跳过本认证类

        try:
            user = User.objects.get(profile__token=token)
        except User.DoesNotExist:
            raise AuthenticationFailed("无效的 Token")

        return (user, token)  # request.user = user, request.auth = token

注册到 settings 或视图的 authentication_classes 中即可生效。

Token 认证快速配置

DRF 内置了 Token 认证方案,适合前后端分离项目:

# settings.py
INSTALLED_APPS = [
    ...
    'rest_framework.authtoken',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ]
}
python manage.py migrate   # 创建 authtoken 表

为用户生成 Token:

from rest_framework.authtoken.models import Token
token, created = Token.objects.get_or_create(user=user)
print(token.key)

客户端在请求头中携带:

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
生产项目通常使用 JWT(djangorestframework-simplejwt)替代内置 Token,支持无状态、自动过期、刷新等特性。

权限(Permissions)

权限负责决定"这个用户有没有资格访问这个接口"。在认证确定用户身份后,权限类进行访问控制。

内置权限类

权限类说明
AllowAny允许所有人访问(默认)
IsAuthenticated仅已认证用户可访问
IsAdminUser仅管理员(is_staff=True)可访问
IsAuthenticatedOrReadOnly已认证用户可读写;匿名用户只读(GET/HEAD/OPTIONS)

全局配置

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

视图级局部覆盖

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

class StudentAPIView(APIView):
    permission_classes = [IsAuthenticated]

自定义权限类

继承 BasePermission 并实现以下方法之一或全部:

  • has_permission(request, view):控制能否访问视图(列表级)。
  • has_object_permission(request, view, obj):控制能否访问具体数据对象(详情级,需调用 get_object() 触发)。
from rest_framework.permissions import BasePermission, SAFE_METHODS


class IsOwnerOrReadOnly(BasePermission):
    """只有资源拥有者才能修改/删除,其他人只读"""

    def has_object_permission(self, request, view, obj):
        # 安全方法(GET, HEAD, OPTIONS)所有人可用
        if request.method in SAFE_METHODS:
            return True
        # 写操作只允许资源的创建者
        return obj.owner == request.user


class IsStaffOrCreator(BasePermission):
    """管理员或创建者可写,其他人只读"""

    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return request.user.is_staff or obj.created_by == request.user

多个权限类之间是关系,所有权限类都返回 True 才放行:

permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

限流(Throttling)

限流控制接口的访问频率,防止滥用(如暴力破解、API 爬虫、高并发攻击)。

内置限流类

限流类说明
AnonRateThrottle匿名用户,按 IP 区分,使用 anon 速率
UserRateThrottle认证用户,按 user ID 区分,使用 user 速率
ScopedRateThrottle针对视图的命名范围(throttle_scope)单独配置速率

全局配置

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',    # 匿名用户每天最多 100 次
        'user': '1000/day',   # 认证用户每天最多 1000 次
    }
}

速率格式:次数/周期,周期可以是 seconds)、minutem)、hourh)、dayd)。

视图级局部配置

from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class LoginView(APIView):
    throttle_classes = [UserRateThrottle]

按视图范围限流(ScopedRateThrottle)

当不同接口需要不同频率限制时使用 ScopedRateThrottle

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.ScopedRateThrottle'],
    'DEFAULT_THROTTLE_RATES': {
        'login':    '5/minute',   # 登录接口:1 分钟最多 5 次
        'contacts': '100/hour',   # 联系人接口:1 小时最多 100 次
    }
}
class LoginView(APIView):
    throttle_scope = 'login'

class ContactListView(ListAPIView):
    throttle_scope = 'contacts'

自定义限流类

from rest_framework.throttling import SimpleRateThrottle


class IPRateThrottle(SimpleRateThrottle):
    """按 IP 区分的限流,速率从 settings 中读取 ip 键"""
    scope = 'ip'

    def get_cache_key(self, request, view):
        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request),  # 获取客户端 IP
        }

三者协同工作流程

HTTP 请求
dispatch()
    ├─ 认证(authentication_classes)
    │    → 设置 request.user / request.auth
    ├─ 权限(permission_classes)
    │    → has_permission() 通过?否则 403
    ├─ 限流(throttle_classes)
    │    → allow_request() 通过?否则 429
视图方法(get / post / put / delete ...)
    └─ 详情操作:get_object() → has_object_permission()
has_object_permission() 只有在视图调用 self.get_object() 时才会触发,直接通过 ORM 查询数据不会执行对象级权限检查。
最后更新于