Пользовательская аутентификация JWT в Django Rest Framework

Я создал приложение Django, в котором я хочу иметь возможность аутентифицировать пользователей, проверяя не только имя пользователя и пароль, но также определенное поле в связанной модели. Пользовательское тело запроса, которое я хочу POST передать в конечную точку:

payload = { 'username': user, 'password': password, 'app_id': uuid4}

Я использую модуль djangorestframework-simplejwt для получения токена доступа.

models.py

class Application(models.Model):

    app_name  = models.CharField(max_length=300)
    app_id    = models.UUIDField(default=uuid.uuid4, editable=False)

    def __str__(self):
        return self.app_name

class ProfileApp(models.Model):

    user       = models.OneToOneField(User, on_delete=models.CASCADE)
    app        = models.ForeignKey(Application, on_delete=models.CASCADE)
    expires_on = models.DateTimeField(default=datetime.now() + timedelta(days=15))

    def __str__(self):
        return self.app.app_name + " | "  + self.user.username

Можно ли изменить TokenObtainPairView с rest_framework_simplejwt для аутентификации пользователя только в том случае, если дата expires_on еще не истекла? Или это проблема архитектуры?


person afonso    schedule 10.07.2019    source источник
comment
В вашем файле urls в настоящее время используется только значение по умолчанию TokenObtainPairView.as_view()?   -  person Henry Woody    schedule 11.07.2019
comment
Да, просто используя значение по умолчанию TokenObtainPairView.as_view()   -  person afonso    schedule 11.07.2019


Ответы (1)


Это можно сделать, создав настраиваемый сериализатор, наследующий от TokenObtainPairSerializer, и расширив метод validate для проверки значений настраиваемых полей. Нет проблем с архитектурой, если вы будете осторожны, чтобы не переопределить необходимые функции родительского класса.

Вот пример:

import datetime as dt
import json
from rest_framework import exceptions
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView



class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    def validate(self, attrs):
        try:
            request = self.context["request"]
        except KeyError:
            pass
        else:
            request_data = json.loads(request.body)
            username = request_data.get("username")
            app_id = request_data.get("app_id")

            profile_has_expired = False
            try:
                profile = ProfileApp.objects.get(user__username=username, app__app_id=app_id)
            except ProfileApp.DoesNotExist:
                profile_has_expired = True
            else:
                profile_has_expired = dt.date.today() > profile.expires_on
            finally:
                if profile_has_expired:
                    error_message = "This profile has expired"
                    error_name = "expired_profile"
                    raise exceptions.AuthenticationFailed(error_message, error_name)
        finally:
            return super().validate(attrs)


class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

Затем используйте MyTokenObtainPairView вместо TokenObtainPairView в вашем файле URL.

Кроме того, поскольку User и ProfileApp совместно используют однозначное поле, похоже, вы можете вообще не использовать ключ / поле "app_id ".

Исходный исходный файл: person Henry Woody    schedule 10.07.2019

comment
С некоторыми настройками (от request.body до request.data) и datetime сравнение часового пояса, я заставил его работать так, как хотелось. Спасибо! - person afonso; 11.07.2019