如何使用自定义权限类发送401错误代码而不是403?

时间:2016-02-29 15:18:27

标签: python django-rest-framework

我使用的是自定义身份验证方案,我无法弄清楚如何让它发送401 HTTP响应而不是403. http://www.django-rest-framework.org/api-guide/authentication/#custom-authentication上的指南说要覆盖authenticate_header方法,但这并不是&# 39;似乎什么也做不了。发送403的部分是引发AuthenticationFailed异常的地方。我通过permission_classes属性将OwnerCredentialsFound分配给了ModelViewSet。

from rest_framework import exceptions
from rest_framework import permissions
from django.contrib.auth.models import User

def authenticateUser(username, password):
    try:
        user = User.objects.get(username=username)
        return user.check_password(password)
    except:
        return False



class OwnerCredentialsFound(permissions.IsAuthenticated):

    def has_permission(self, request, view):
        #Check credentials
        #If the fields don't exist, the given default value is used
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        authenticated = authenticateUser(username, password)
        if(not authenticated and username is not None and password is not None):
            raise exceptions.AuthenticationFailed('Username/password pair not found')
        elif(not authenticated):
            authenticated = permissions.IsAuthenticated.has_permission(self, request, view)
        else:
            #set the user
            view.request.user = User.objects.get(username=username)
        return authenticated

    def authenticate_header(self, request):
        return '{"username" : <username>, "password" : <password>}'

更新:似乎我已经混淆了身份验证和权限类。我使用的是权限类,但是它的身份验证类有一个名为authenticate_header的方法。

2 个答案:

答案 0 :(得分:2)

当您从False返回has_permission时,DRF会引发PermissionDenied异常。然后在名为exception_hanlder的函数中捕获并处理异常,如下所示:

elif isinstance(exc, PermissionDenied):
    msg = _('Permission denied.')
    data = {'detail': six.text_type(msg)}

    set_rollback()
    return Response(data, status=status.HTTP_403_FORBIDDEN)

看起来您可以定义一些自定义异常,在OwnerCredentialsFound.has_permission中将其抛出错误,然后使用自定义异常处理程序自己捕获它。请在这里阅读更多: http://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling

答案 1 :(得分:1)

基本上,我并没有真正了解权限和身份验证之间的区别,因此导致了混淆。权限类没有authenticate_header方法,但身份验证类具有。以下是我为解决问题所做的工作:

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

def authenticateUser(username, password):
    try:
        user = User.objects.get(username=username)
        return user.check_password(password)
    except:
        return False

class CustomAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        authenticated = authenticateUser(username, password)
        if(not authenticated and username is not None and password is not None):
            #authentication attempted and failed
            raise exceptions.AuthenticationFailed('Username/password pair not found')
        elif(not authenticated):
            #authentication not attempted (try other authentications)
            return None
        else:
            #authentication attempted and suceeded
            return (User.objects.get(username=username), None)


    def authenticate_header(self, request):
        return '{"username" : <username>, "password" : <password>}'

在我看来:

permission_classes = (IsAuthenticated,)
authentication_classes = (CustomAuthentication, SessionAuthentication)

这种权限和身份验证的混淆也解释了为什么我尝试组合多个权限类失败(您可能会在我的原始代码中注意到我从权限类继承并调用其has_permission方法以解决此问题)。我不再需要自定义权限类,因为我可以使用两个身份验证类。