7

私はそのような認証クラスを作成しました:

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
}

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

restapi/tests.py

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

私のテストでは、例外がありますAttributeError: 'WSGIRequest' object has no attribute 'successful_authenticator'

なぜこのエラーが発生するのですか? 修正方法は?

4

2 に答える 2

4

私にとって、これはカスタム認証クラスとは何の関係もありませんでしたが、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)

関数は、self.get_object私が興味を持っていた 2 つのことを呼び出します。主に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()

また、関数への呼び出しself.check_object_permissionsと関数内での手動呼び出しを移動しようとしましたが、役に立ちませんでした。1 つはもう 1 つを必要とします (許可チェックは反対する必要があり、オブジェクトを取得するには 404 チェックを行う必要があります)。そして、許可と 404 チェックの前にスーパーを呼び出そうとすると、関数が最初に呼び出されることを意味し、基本的に最初に関数で取得しようとしていた効果が無効になります。したがって、私が知る限り、これがこの場合に解決する唯一の方法です。get_object_or_404dispatch()get()dispatch

興味深い情報については、django-rest-framework に関するこの問題も参照してください: https://github.com/encode/django-rest-framework/issues/918

于 2018-08-08T13:14:47.430 に答える