在 Python 3 中,Django 的 APIView
类是 Django REST Framework(DRF)中用于构建 API 视图的核心基类。它提供了一个灵活的框架来处理 HTTP 请求,并通过一系列方法支持认证、权限检查和请求限制等功能。
self.perform_authentication(request)
:认证self.check_permissions(request)
:权限验证self.check_throttles(request)
:请求速率限制
1. self.perform_authentication(request)
作用
self.perform_authentication(request)
用于对传入的 HTTP 请求进行身份认证(authentication)。它的主要目的是验证请求是否来自合法用户,并将认证后的用户信息(如用户对象)绑定到 request.user
和 request.auth
属性上。
request.user
: 通常存储认证后的用户对象(例如 Django 的User
模型实例)。如果未认证,默认为AnonymousUser
。request.auth
: 存储认证过程中可能附加的认证信息,例如 JWT 令牌、OAuth 令牌等。
实现原理
perform_authentication
方法调用了 DRF 的认证流程,依赖于 authentication_classes
属性中定义的认证类。DRF 的认证机制是模块化的,允许开发者通过配置不同的认证类(如 SessionAuthentication
、TokenAuthentication
、JWTAuthentication
等)来支持多种认证方式。
在 APIView
类中,perform_authentication
的定义如下(参考 DRF 源码,基于 3.15 版本):
def perform_authentication(self, request):
request.user
看似简单,但实际逻辑隐藏在 request.user
的属性访问中。当访问 request.user
时,DRF 会触发以下步骤:
- 获取认证类:从
self.authentication_classes
获取配置的认证类列表。这些类继承自rest_framework.authentication.BaseAuthentication
。 - 逐个调用认证类:按顺序调用每个认证类的
authenticate
方法,尝试对请求进行认证。- 每个
authenticate
方法返回一个(user, auth)
元组,表示认证成功;如果返回None
,则尝试下一个认证类。 - 如果认证失败且认证类抛出
AuthenticationFailed
异常,DRF 会立即返回 401 Unauthorized 响应。
- 每个
- 设置认证结果:
- 如果认证成功,
request.user
被设置为认证后的用户对象,request.auth
被设置为认证令牌或其他上下文信息。 - 如果没有认证类成功认证,
request.user
默认为AnonymousUser
,request.auth
为None
。
- 如果认证成功,
执行时机
perform_authentication
在 APIView
的 initial
方法中被调用,initial
方法是请求处理流程的初始化阶段:
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
# 其他初始化逻辑
配置认证类
在 APIView
子类中,可以通过 authentication_classes
属性指定认证方式。例如:
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
class MyAPIView(APIView):
authentication_classes = [SessionAuthentication, TokenAuthentication]
def get(self, request):
return Response({"user": request.user.username})
在上述例子中,DRF 会优先尝试 SessionAuthentication
,如果失败则尝试 TokenAuthentication
。
注意事项
- 短路行为:
perform_authentication
不会直接抛出异常,而是依赖认证类的实现。如果所有认证类都返回None
,请求被视为匿名请求。 - 性能考虑:认证可能涉及数据库查询(如验证用户令牌),因此应尽量优化认证类的实现,避免不必要的性能开销。
- 自定义认证:可以继承
BaseAuthentication
创建自定义认证类,并添加到authentication_classes
中。例如:
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class CustomAuth(BaseAuthentication):
def authenticate(self, request):
auth_header = request.headers.get('Authorization')
if not auth_header:
return None
# 自定义认证逻辑
user = validate_token(auth_header) # 假设的 token 验证函数
if not user:
raise AuthenticationFailed('Invalid token')
return (user, auth_header)
常见使用场景
- 基于令牌的认证:如
TokenAuthentication
或 JWT,用于移动端或单页应用。 - 会话认证:如
SessionAuthentication
,适用于基于浏览器的应用。 - 匿名访问:允许匿名用户访问某些 API(
request.user
为AnonymousUser
)。
2. self.check_permissions(request)
作用
self.check_permissions(request)
用于检查请求用户是否具有执行当前操作的权限。它基于 permission_classes
属性中定义的权限类,决定是否允许请求继续处理。如果权限检查失败,DRF 会抛出 PermissionDenied
异常,导致返回 403 Forbidden 响应。
实现原理
check_permissions
方法会遍历 self.permission_classes
中定义的权限类,调用每个权限类的 has_permission
方法:
def check_permissions(self, request):
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
- 权限类:每个权限类继承自
rest_framework.permissions.BasePermission
,并实现has_permission
方法(对于对象级权限,还可能实现has_object_permission
)。 - 检查逻辑:
has_permission
方法返回True
表示权限通过,False
表示拒绝。如果任何一个权限类返回False
,DRF 会抛出PermissionDenied
异常。 - 错误处理:
permission_denied
方法会生成 403 响应,并包含权限类提供的错误信息(如果有)。
执行时机
check_permissions
在 initial
方法中,在 perform_authentication
之后调用。这是因为权限检查通常依赖于认证结果(例如,检查 request.user
是否有特定权限)。
配置权限类
可以通过 permission_classes
属性指定权限。例如:
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, IsAdminUser
class MyAPIView(APIView):
permission_classes = [IsAuthenticated, IsAdminUser]
def get(self, request):
return Response({"message": "You have admin access"})
在上述例子中,用户必须同时通过 IsAuthenticated
(已登录)和 IsAdminUser
(管理员权限)检查才能访问 API。
内置权限类
DRF 提供了一些常用的权限类:
AllowAny
: 允许所有用户(包括匿名用户)访问。IsAuthenticated
: 仅允许已认证用户访问。IsAdminUser
: 仅允许管理员用户访问。IsAuthenticatedOrReadOnly
: 允许匿名用户的只读访问(GET、HEAD、OPTIONS),但写操作需要认证。
自定义权限
可以创建自定义权限类。例如,限制只有特定角色的用户可以访问:
from rest_framework.permissions import BasePermission
class IsPremiumUser(BasePermission):
def has_permission(self, request, view):
return request.user.is_authenticated and request.user.is_premium
注意事项
- 权限顺序:权限类按顺序检查,建议将更严格的权限放在前面以提高效率。
- 对象级权限:对于需要检查特定对象的权限(如编辑某个资源),可以在视图中调用
self.check_object_permissions(request, obj)
,触发权限类的has_object_permission
方法。 - 错误信息:可以通过权限类的
message
属性自定义错误提示。例如:
class IsPremiumUser(BasePermission):
message = "You must be a premium user to access this resource."
def has_permission(self, request, view):
return request.user.is_authenticated and request.user.is_premium
常见使用场景
- 限制访问:仅允许认证用户或特定角色(如管理员、订阅者)访问 API。
- 只读访问:允许匿名用户读取数据,但限制写操作。
- 动态权限:根据请求方法(GET、POST 等)或资源类型动态调整权限。
3. self.check_throttles(request)
作用
self.check_throttles(request)
用于限制请求速率(rate limiting),防止 API 被滥用。它基于 throttle_classes
属性中定义的限制类,检查用户或 IP 的请求频率是否超出限制。如果超出限制,DRF 会抛出 Throttled
异常,导致返回 429 Too Many Requests 响应。
实现原理
check_throttles
方法会遍历 self.throttle_classes
中定义的限制类,调用每个限制类的 allow_request
方法:
def check_throttles(self, request):
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
- 限制类:每个限制类继承自
rest_framework.throttling.BaseThrottle
,并实现allow_request
方法,决定是否允许当前请求。 - 检查逻辑:
allow_request
返回True
表示请求允许,False
表示请求被限制。如果被限制,throttle.wait()
返回预计的等待时间(秒),并包含在 429 响应中。 - 缓存支持:DRF 的限制机制通常依赖缓存(如 Redis 或 Django 的内存缓存)来跟踪请求频率。
执行时机
check_throttles
在 initial
方法中,在 perform_authentication
和 check_permissions
之后调用。这是因为速率限制通常需要基于用户身份或 IP 地址,而这些信息在认证后更明确。
配置限制类
可以通过 throttle_classes
属性指定限制。例如:
from rest_framework.views import APIView
from rest_framework.throttling import UserRateThrottle
class MyAPIView(APIView):
throttle_classes = [UserRateThrottle]
def get(self, request):
return Response({"message": "Request allowed"})
内置限制类
DRF 提供了一些常用的限制类:
AnonRateThrottle
: 限制匿名用户的请求速率,基于 IP 地址。UserRateThrottle
: 限制认证用户的请求速率,基于用户 ID。ScopedRateThrottle
: 限制特定视图的请求速率,可以通过throttle_scope
属性配置。
限制速率在 settings.py
中通过 REST_FRAMEWORK
配置。例如:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day', # 匿名用户每天 100 次请求
'user': '1000/day', # 认证用户每天 1000 次请求
}
}
自定义限制
可以创建自定义限制类。例如,限制特定用户组的请求速率:
from rest_framework.throttling import SimpleRateThrottle
class PremiumUserThrottle(SimpleRateThrottle):
scope = 'premium'
def get_cache_key(self, request, view):
if request.user.is_authenticated and request.user.is_premium:
return f"throttle_{request.user.id}"
return None
在 settings.py
中配置:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'premium': '5000/day',
}
}
注意事项
- 缓存依赖:速率限制通常需要配置缓存后端(如 Redis),否则可能无法正常工作。
- 匿名用户限制:基于 IP 的限制可能不准确,因为多个用户可能共享同一 IP(如代理服务器)。
- 动态限制:可以通过
throttle_scope
和自定义限制类实现基于视图或用户的动态限制。 - 错误响应:429 响应会包含
Retry-After
头,指示客户端需要等待的时间。
常见使用场景
- 防止滥用:限制匿名用户对公开 API 的频繁访问。
- 分级服务:为不同用户组(如免费用户、付费用户)设置不同的请求速率。
- 保护资源:避免服务器因高频请求过载。
综合分析与优化建议
执行顺序的意义
perform_authentication
、check_permissions
和 check_throttles
的调用顺序是有意设计的:
- 先认证:确保
request.user
和request.auth
已设置,因为权限和限制可能依赖这些信息。 - 再检查权限:基于认证结果判断用户是否有权访问资源。
- 最后限制速率:确保只有合法请求才会计入速率限制,避免浪费配额。
性能优化
- 认证:选择轻量级的认证方式(如 JWT 比 Session 更适合无状态 API),并缓存频繁查询的用户数据。
- 权限:将复杂的权限检查逻辑移到对象级(
has_object_permission
),减少视图级检查的开销。 - 限制:使用高效的缓存后端(如 Redis),并合理设置限制周期(如小时、天)以平衡性能和安全性。
扩展性
- 模块化设计:DRF 的认证、权限和限制机制高度模块化,允许通过自定义类实现复杂逻辑。
- 全局配置:在
settings.py
中通过REST_FRAMEWORK
设置默认的认证、权限和限制类,减少重复代码。 - 动态调整:通过
get_authenticators
、get_permissions
和get_throttles
方法动态返回认证、权限或限制类,实现基于视图或请求的灵活配置。
常见问题与解决方案
- 401 vs. 403:
- 401 Unauthorized:认证失败(如无效的令牌)。
- 403 Forbidden:认证成功但权限不足。
- 解决方案:确保认证和权限类的错误信息清晰,方便调试。
- 速率限制不生效:检查是否正确配置了缓存后端(如
CACHES
设置)。 - 匿名用户问题:明确区分匿名和认证用户的处理逻辑,避免意外允许敏感操作。
实际案例
假设你正在开发一个博客 API,需求如下:
- 匿名用户可以读取文章(GET)。
- 认证用户可以创建文章(POST),但每天限制 10 次请求。
- 管理员可以编辑所有文章(PUT)。
实现代码如下:
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.throttling import UserRateThrottle
from rest_framework.response import Response
class ArticleAPIView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
throttle_classes = [UserRateThrottle]
def get_permissions(self):
if self.request.method == 'GET':
return [AllowAny()]
elif self.request.method == 'PUT':
return [IsAdminUser()]
return super().get_permissions()
def get(self, request):
return Response({"message": "List of articles"})
def post(self, request):
return Response({"message": "Article created"})
def put(self, request):
return Response({"message": "Article updated"})
在 settings.py
中:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'user': '10/day',
}
}
总结
perform_authentication
:负责用户认证,将用户信息绑定到请求对象,依赖authentication_classes
。check_permissions
:检查用户权限,决定是否允许访问,依赖permission_classes
。check_throttles
:限制请求速率,防止滥用,依赖throttle_classes
。- 设计哲学:DRF 通过模块化的认证、权限和限制机制,提供灵活且可扩展的 API 开发框架。
- 优化与实践:合理配置认证、权限和限制类,结合缓存和动态调整,满足复杂业务需求。