Ben*_*ueg 6 authentication django token django-rest-framework
我已经创建了一个认证类:
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'
为什么我有这个错误?怎么解决?
问题来自这条线:
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)
对我来说,这与自定义身份验证类无关,而是与我的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_permissions和get_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
| 归档时间: |
|
| 查看次数: |
5771 次 |
| 最近记录: |