'WSGIRequest'对象没有属性'successful_authenticator'

Ben*_*ueg 6 authentication django token django-rest-framework

我已经创建了一个认证类:

RESTful API的令牌认证:令牌是否应定期更改?

restapi/settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        # 'rest_framework.authentication.TokenAuthentication',
        'restapi.authentication.ExpiringTokenAuthentication',
    ),
    'PAGINATE_BY': 10
}
Run Code Online (Sandbox Code Playgroud)

restapi/authentication.py

import datetime
from rest_framework.authentication import TokenAuthentication

class ExpiringTokenAuthentication(TokenAuthentication):
    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')

        # This is required for the time comparison
        utc_now = datetime.utcnow()
        utc_now = utc_now.replace(tzinfo=pytz.utc)

        if token.created < utc_now - timedelta(hours=24):
            raise exceptions.AuthenticationFailed('Token has expired')

        return token.user, token
Run Code Online (Sandbox Code Playgroud)

restapi/tests.py

def test_get_missions(self):
    """
    Tests that /missions/ returns with no error
    """
    response = self.client.get('/missions/', HTTP_AUTHORIZATION = self.auth)
Run Code Online (Sandbox Code Playgroud)

在我的测试中,我有一个例外 AttributeError: 'WSGIRequest' object has no attribute 'successful_authenticator'

为什么我有这个错误?怎么解决?

Ben*_*ueg 6

问题来自这条线:

utc_now = datetime.utcnow()
Run Code Online (Sandbox Code Playgroud)

这导致AttributeError: 'WSGIRequest' object has no attribute 'successful_authenticator'.

已经有一段时间了,因为我偶然发现了这样一个误导性的错误信息.

这是我解决它的方式:

restapi/authentication.py

import datetime
from django.utils.timezone import utc
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions

class ExpiringTokenAuthentication(TokenAuthentication):
    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')

        utc_now = datetime.datetime.utcnow().replace(tzinfo=utc)

        if token.created < utc_now - datetime.timedelta(hours=24):
            raise exceptions.AuthenticationFailed('Token has expired')

        return (token.user, token)
Run Code Online (Sandbox Code Playgroud)


Bon*_*ono 5

对我来说,这与自定义身份验证类无关,而是与我的dispatch()函数中定义的方法调用的处理方式有关。例如,我有以下设置:

class MyView(GenericAPIView):
    queryset = MyModel.objects.all()
    lookup_field = 'my_field'

    def dispatch(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super().dispatch(request, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

函数self.get_object调用了我感兴趣的两个东西。主要是函数self.check_object_permissionsget_object_or_404函数。check_object_permissions对我来说,如果函数返回,它就会出错False。这将导致调用,self.permission_denied()然后尝试访问successful_authenticator请求上的属性。但由于请求未设置为该rest_framework.request.Request属性,因此该属性将不存在。这是因为该dispatch方法在其运行过程中设置了这个正确的请求。由于这是在调用后完成的,因此永远self.get_object()不会正确设置。

就我个人而言,这让我感到非常惊讶,因为该dispatch()函数通常用于确定某些关键事情是否真实(即对象是否存在以及用户是否具有权限)。如果一切正常,我们将继续处理该请求。但显然这种观点没有办法做到这一点。我解决这个问题的唯一方法是将调用移至self.get_object()我的get()方法,如下所示:

class MyView(GenericAPIView):
    queryset = MyModel.objects.all()
    lookup_field = 'my_field'

    def get(self, request, *args, **kwargs):
        my_object = self.get_object()
Run Code Online (Sandbox Code Playgroud)

我还尝试在函数self.check_object_permissions中移动手动调用,但无济于事。一个需要另一个(权限检查需要对象,并且要获取对象,您必须进行 404 检查)。尝试在权限和 404 检查之前调用 super 意味着该函数将首先被调用,基本上否定了您首先尝试在函数中获得的效果。据我所知,这是解决这种情况的唯一方法。get_object_or_404dispatch()get()dispatch

另请参阅 django-rest-framework 上的此问题以获取一些有趣的信息:https://github.com/encode/django-rest-framework/issues/918