【问题标题】:Django Rest Framework Custom JWT authenticationDjango Rest Framework 自定义 JWT 身份验证
【发布时间】:2019-11-20 12:52:50
【问题描述】:

我创建了一个 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

是否可以从 rest_framework_simplejwt 覆盖 TokenObtainPairView 以仅在 expires_on 日期仍未过期时验证用户?或者这样做是否存在架构问题?

【问题讨论】:

  • 您的 urls 文件目前是否只使用默认的TokenObtainPairView.as_view()
  • 是的,只是使用默认的TokenObtainPairView.as_view()

标签: python django django-rest-framework django-rest-framework-simplejwt


【解决方案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

然后在您的 urls 文件中使用 MyTokenObtainPairView 代替 TokenObtainPairView

此外,由于 UserProfileApp 共享一个一对一的字段,看起来您完全可以不使用 "app_id" 键/字段。

原始源文件:https://github.com/davesque/django-rest-framework-simplejwt/blob/04376b0305e8e2fda257b08e507ccf511359d04a/rest_framework_simplejwt/serializers.py

【讨论】:

  • 通过一些调整(request.bodyrequest.data)并进行 datetime 比较时区感知,我已经让它按需要工作。谢谢!