在 Django REST Framework (DRF) 中,令牌身份验证是一种常见的 API 认证方式,它通过验证客户端提供的令牌来识别用户身份。有时我们需要根据不同的请求类型(GET/POST/PUT/DELETE等)应用不同的认证策略。
创建一个自定义认证类,继承 rest_framework.authentication.TokenAuthentication
,并重写 authenticate
方法:
from rest_framework.authentication import TokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
class ConditionalTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
# 只在特定请求类型下进行令牌验证
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
return super().authenticate(request)
# 对于GET请求,跳过令牌验证
return None
在视图类中指定认证类:
from rest_framework.views import APIView
from rest_framework.response import Response
class MyView(APIView):
authentication_classes = [ConditionalTokenAuthentication]
def get(self, request):
# GET请求不需要令牌
return Response({"message": "This is a public GET request"})
def post(self, request):
# POST请求需要令牌验证
return Response({"message": "This is a protected POST request"})
对于函数视图,可以使用 @authentication_classes
装饰器:
from rest_framework.decorators import authentication_classes, api_view
from rest_framework.response import Response
@api_view(['GET', 'POST'])
@authentication_classes([ConditionalTokenAuthentication])
def my_view(request):
if request.method == 'GET':
return Response({"message": "Public GET"})
else:
return Response({"message": "Protected POST"})
原因:可能没有正确设置认证类或请求方法判断有误
解决:检查视图的 authentication_classes
设置和自定义认证类的逻辑
原因:即使认证通过,可能缺少相应权限类
解决:确保设置了适当的 permission_classes
,例如:
from rest_framework.permissions import IsAuthenticated, AllowAny
class MyView(APIView):
authentication_classes = [ConditionalTokenAuthentication]
permission_classes = [AllowAny] # 默认允许所有
def get_permissions(self):
if self.request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
return [IsAuthenticated()]
return super().get_permissions()
原因:DRF 在处理 OPTIONS 方法时也会调用认证 解决:在自定义认证类中明确处理 OPTIONS 方法:
class ConditionalTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
if request.method in ['OPTIONS']:
return None
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
return super().authenticate(request)
return None
# authentication.py
from rest_framework.authentication import TokenAuthentication
class MethodBasedTokenAuthentication(TokenAuthentication):
"""
根据请求方法应用令牌认证:
- 安全方法(POST/PUT/PATCH/DELETE)需要认证
- 其他方法(GET/HEAD/OPTIONS)跳过认证
"""
def authenticate(self, request):
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
return super().authenticate(request)
return None
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from .authentication import MethodBasedTokenAuthentication
class ProductAPIView(APIView):
authentication_classes = [MethodBasedTokenAuthentication]
def get_permissions(self):
if self.request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
return [IsAuthenticated()]
return [AllowAny()]
def get(self, request):
return Response({"data": "Public product list"})
def post(self, request):
return Response({"message": "Product created"}, status=201)
# urls.py
from django.urls import path
from .views import ProductAPIView
urlpatterns = [
path('products/', ProductAPIView.as_view()),
]
这种实现方式提供了灵活的安全控制,同时保持了API的易用性。
没有搜到相关的文章