我正在尝试使用 Django Rest Framework 来限制列表视图。
如果我使用错误的令牌发送请求:
Authorization: Token f1cfedb0895105ee088e8aab5bf0ae7ca9752e79
它返回 HTTP 401 错误,这是正确的
但是,如果我只是从HTTP标头中删除授权,它将在不进行身份验证的情况下进行查询并返回 HTTP 200
我在这里做错了什么:
class OrderViewSet(viewsets.ModelViewSet):
serializer_class = OrderSerializer
model = Order
@permission_classes((IsAuthenticated,))
@authentication_classes((TokenAuthentication,))
def list(self, request, *args, **kwargs):
serializer = OrderSerializer(Order.objects.opened(), many=True)
return Response(serializer.data)
此外,create方法不应仅限于经过身份验证的用户。
答案 0 :(得分:2)
我建议您对Viewset进行标准化:
class OrderViewSet(viewsets.ModelViewSet):
serializer_class = OrderSerializer
model = Order
def get_queryset(self):
"""QuerySet for this entire ModelViewSet will only return
orders which are opened.
"""
return Order.objects.opened()
@permission_classes((IsAuthenticated,))
@authentication_classes((TokenAuthentication,))
def list(self, request, *args, **kwargs):
return super(OrderViewSet, self).list(request, *args, **kwargs)
经过进一步调查,我查看了TokenAuthentication的源代码,看来如果您根本没有发送身份验证令牌,authenticate()
方法会返回None
get_authorization_header()
方法什么都不返回。因此,如果您从标题中完全删除HTTP_AUTHORIZATION
,则这是预期的行为。
我认为这里的目的是不提出异常,以便身份验证可以转移到下一个可能的身份验证类。如果这不是您想要执行的操作,则可以覆盖从authenticate()
继承的您自己的类中的TokenAuthentication
方法。请参阅下面的代码。
def get_authorization_header(request):
"""
Return request's 'Authorization:' header, as a bytestring.
Hide some test client ickyness where the header can be unicode.
"""
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth, type('')):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
return auth
class TokenAuthentication(BaseAuthentication):
"""
Simple token based authentication.
Clients should authenticate by passing the token key in the "Authorization"
HTTP header, prepended with the string "Token ". For example:
Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
"""
model = Token
"""
A custom token model may be used, but must have the following properties.
* key -- The string identifying the token
* user -- The user to which the token belongs
"""
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
return None
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid token header. Token string should not contain spaces.'
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(auth[1])
def authenticate_credentials(self, key):
try:
token = self.model.objects.get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
return (token.user, token)
def authenticate_header(self, request):
return 'Token'
最后,要使用401
而不是200
使令牌失败,您可以执行以下操作:
class YourCustomTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid token header. Token string should not contain spaces.'
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(auth[1])