我如何在中使用'email';django rest框架simplejwt“;而不是“username”来生成令牌?

我如何在中使用'email';django rest框架simplejwt“;而不是“username”来生成令牌?,django,django-rest-framework,Django,Django Rest Framework,在django rest框架中,默认情况下使用simplejwt插件用户名和密码。但是我想用电子邮件而不是用户名。因此,我确实喜欢以下内容: 在序列化程序中: class MyTokenObtainSerializer(Serializer): username_field = User.EMAIL_FIELD def __init__(self, *args, **kwargs): super(MyTokenObtainSerializer, self).__

在django rest框架中,默认情况下使用simplejwt插件
用户名
密码
。但是我想用
电子邮件
而不是
用户名
。因此,我确实喜欢以下内容:

在序列化程序中:

class MyTokenObtainSerializer(Serializer):
    username_field = User.EMAIL_FIELD

    def __init__(self, *args, **kwargs):
        super(MyTokenObtainSerializer, self).__init__(*args, **kwargs)

        self.fields[self.username_field] = CharField()
        self.fields['password'] = PasswordField()

    def validate(self, attrs):
        # self.user = authenticate(**{
        #     self.username_field: attrs[self.username_field],
        #     'password': attrs['password'],
        # })
        self.user = User.objects.filter(email=attrs[self.username_field]).first()
        print(self.user)

        if not self.user:
            raise ValidationError('The user is not valid.')

        if self.user:
            if not self.user.check_password(attrs['password']):
                raise ValidationError('Incorrect credentials.')
        print(self.user)
        # Prior to Django 1.10, inactive users could be authenticated with the
        # default `ModelBackend`.  As of Django 1.10, the `ModelBackend`
        # prevents inactive users from authenticating.  App designers can still
        # allow inactive users to authenticate by opting for the new
        # `AllowAllUsersModelBackend`.  However, we explicitly prevent inactive
        # users from authenticating to enforce a reasonable policy and provide
        # sensible backwards compatibility with older Django versions.
        if self.user is None or not self.user.is_active:
            raise ValidationError('No active account found with the given credentials')

        return {}

    @classmethod
    def get_token(cls, user):
        raise NotImplemented(
            'Must implement `get_token` method for `MyTokenObtainSerializer` subclasses')


class MyTokenObtainPairSerializer(MyTokenObtainSerializer):
    @classmethod
    def get_token(cls, user):
        return RefreshToken.for_user(user)

    def validate(self, attrs):
        data = super(MyTokenObtainPairSerializer, self).validate(attrs)

        refresh = self.get_token(self.user)

        data['refresh'] = text_type(refresh)
        data['access'] = text_type(refresh.access_token)

        return data
class MyTokenObtainPairView(TokenObtainPairView):
   """
    Takes a set of user credentials and returns an access and refresh JSON web
    token pair to prove the authentication of those credentials.
   """
    serializer_class = MyTokenObtainPairSerializer
视图中:

class MyTokenObtainSerializer(Serializer):
    username_field = User.EMAIL_FIELD

    def __init__(self, *args, **kwargs):
        super(MyTokenObtainSerializer, self).__init__(*args, **kwargs)

        self.fields[self.username_field] = CharField()
        self.fields['password'] = PasswordField()

    def validate(self, attrs):
        # self.user = authenticate(**{
        #     self.username_field: attrs[self.username_field],
        #     'password': attrs['password'],
        # })
        self.user = User.objects.filter(email=attrs[self.username_field]).first()
        print(self.user)

        if not self.user:
            raise ValidationError('The user is not valid.')

        if self.user:
            if not self.user.check_password(attrs['password']):
                raise ValidationError('Incorrect credentials.')
        print(self.user)
        # Prior to Django 1.10, inactive users could be authenticated with the
        # default `ModelBackend`.  As of Django 1.10, the `ModelBackend`
        # prevents inactive users from authenticating.  App designers can still
        # allow inactive users to authenticate by opting for the new
        # `AllowAllUsersModelBackend`.  However, we explicitly prevent inactive
        # users from authenticating to enforce a reasonable policy and provide
        # sensible backwards compatibility with older Django versions.
        if self.user is None or not self.user.is_active:
            raise ValidationError('No active account found with the given credentials')

        return {}

    @classmethod
    def get_token(cls, user):
        raise NotImplemented(
            'Must implement `get_token` method for `MyTokenObtainSerializer` subclasses')


class MyTokenObtainPairSerializer(MyTokenObtainSerializer):
    @classmethod
    def get_token(cls, user):
        return RefreshToken.for_user(user)

    def validate(self, attrs):
        data = super(MyTokenObtainPairSerializer, self).validate(attrs)

        refresh = self.get_token(self.user)

        data['refresh'] = text_type(refresh)
        data['access'] = text_type(refresh.access_token)

        return data
class MyTokenObtainPairView(TokenObtainPairView):
   """
    Takes a set of user credentials and returns an access and refresh JSON web
    token pair to prove the authentication of those credentials.
   """
    serializer_class = MyTokenObtainPairSerializer
而且很有效

现在我的问题是,我如何才能更有效地做到这一点?有人能对此提出建议吗?提前感谢。

此答案供未来读者参考,因此包含额外信息

为了简化身份验证后端,您有多个类要挂接。我建议做下面的选项1(可选选项3,您的简化版本)。在阅读之前,请注意以下几点:

  • 注1:django不按要求强制发送电子邮件,也不在用户创建时强制发送唯一的电子邮件(您可以忽略这一点,但这是离题的)!因此,选项3(您的实现)可能会给您带来重复电子邮件的问题
  • 注1b:使用
    User.objects.filter(email\uu iexact=…)
    以不区分大小写的方式匹配电子邮件
  • 注1c:使用
    get\u user\u model()
    以防将来替换默认的用户模型,这确实是初学者的救命稻草
  • 注意2:避免将用户打印到控制台。您可能正在打印敏感数据
至于3个选项:

  • 使用f.e.
    类EmailModelBackend(ModelBackend)
    调整django身份验证后端,并替换身份验证功能。
    • 不调整令牌声明
    • 不依赖于JWT类/中间件(SimpleJWT、JWT或其他)
    • 还调整其他身份验证类型(会话/Cookie/非API身份验证等)
    • 所需的输入参数仍然是用户名,示例如下。如果你不喜欢,调整一下,但要小心。(可能会破坏您的导入/插件,这不是必需的!)
  • 替换django
    authenticate(用户名=,密码=,**kwarg)
    fromdjango.contrib.auth
    • 不调整令牌声明
    • 您还需要替换令牌后端,因为它应该使用不同的身份验证,正如上面所做的那样
    • 不使用
      authenticate(…)
      调整其他应用程序,仅替换JWT auth(如果您将其设置为JWT auth) 不需要参数,因此此选项不太受欢迎)
  • 使用电子邮件作为声明实现MyTokenActainPairSerializer。
    • 现在,电子邮件作为JWT数据(而不是id)发回
    • 与选项1一起,您的应用程序身份验证变得与用户名无关
  • 选项1(注意,这也允许用户名!!):

    选项2: 已跳过,留给读者,未通知

    选项3: 你似乎已经在上面讲过了

    注意:您不必定义
    MyTokenActainPairView
    ,您可以在URL.py中使用
    TokenActainPairView(serializer\u class=MyTokenActainPairSerializer)。覆盖使用的令牌序列化程序的小型简化

    注意2:您可以在settings.py(或settings文件)中指定识别声明和添加的数据,以使用电子邮件。这将使您的前端应用程序也使用电子邮件进行索赔(而不是默认的用户.id)

    但是,请注意创作者给出的唯一性警告:

    例如,指定“用户名”或“电子邮件”字段将是一个糟糕的选择,因为帐户的用户名或电子邮件可能会根据给定服务中帐户管理的设计方式而改变


    如果你能保证唯一性,你就一切就绪。

    为什么你要复制粘贴这么多而不是子类化?我用它工作:

    # serializers.py 
    from rest_framework_simplejwt.serializers import TokenObtainSerializer
    
    class EmailTokenObtainSerializer(TokenObtainSerializer):
        username_field = User.EMAIL_FIELD
    
    
    class CustomTokenObtainPairSerializer(EmailTokenObtainSerializer):
        @classmethod
        def get_token(cls, user):
            return RefreshToken.for_user(user)
    
        def validate(self, attrs):
            data = super().validate(attrs)
    
            refresh = self.get_token(self.user)
    
            data["refresh"] = str(refresh)
            data["access"] = str(refresh.access_token)
    
            return data
    
    

    当然

    #urls.py
    from rest_framework_simplejwt.views import TokenRefreshView
    from .views import EmailTokenObtainPairView
    
    url("token/", EmailTokenObtainPairView.as_view(), name="token_obtain_pair"),
    url("refresh/", TokenRefreshView.as_view(), name="token_refresh"),
    
    

    除了@Mic的答案,记住设置
    USERNAME\u FIELD='email'
    ,在用户模型中可能是
    REQUIRED\u FIELDS=['USERNAME']

    这个问题已经问了一段时间了,但是我为@Mic的答案添加了+1。顺便问一下,仅按以下方式更新到
    TokenOccessPairSerializer
    是否足够

    from rest_framework_simplejwt.views import TokenObtainPairView
    from rest_framework_simplejwt.serializers import (
        TokenObtainPairSerializer, User
    )
    
    class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
        username_field = User.EMAIL_FIELD
    
    
    class EmailTokenObtainPairView(TokenObtainPairView):
        serializer_class = CustomTokenObtainPairSerializer
    
    

    让我们总结一下上述解决方案:

    1-使用Django命令创建两个应用程序。一个用于新令牌,另一个用于用户:

    python manage.py startapp m_token  # modified token
    python manage.py startapp m_user  # modified user
    
    2-在m_令牌中,创建serializers.py并重写serializer以将用户名替换为电子邮件字段:

    # serializers.py
    from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, User
    
    
    class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
            username_field = User.EMAIL_FIELD
    
    3-在m_标记中,重写视图以用新的序列化程序替换序列化程序:

    # views.py
    from rest_framework_simplejwt.views import TokenObtainPairView
    from .serializer import CustomTokenObtainPairSerializer
    
    
    class EmailTokenObtainPairView(TokenObtainPairView):
        serializer_class = CustomTokenObtainPairSerializer
    
    4-在m_令牌中,创建url.py并按如下方式给出路径:

    # urls.py
    from django.urls import path
    from .views import TokenObtainPairView
    from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
    
    
    urlpatterns = [
        path(r'token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path(r'token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
        path(r'token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    ]
    
    # models.py
    from django.contrib.auth.models import AbstractUser
    
    
    class MUser(AbstractUser):
        USERNAME_FIELD = 'email'
        EMAIL_FIELD = 'email'
        REQUIRED_FIELDS = ['username']
    
    5-在m_用户中,按如下方式覆盖用户模型:

    # urls.py
    from django.urls import path
    from .views import TokenObtainPairView
    from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
    
    
    urlpatterns = [
        path(r'token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path(r'token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
        path(r'token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    ]
    
    # models.py
    from django.contrib.auth.models import AbstractUser
    
    
    class MUser(AbstractUser):
        USERNAME_FIELD = 'email'
        EMAIL_FIELD = 'email'
        REQUIRED_FIELDS = ['username']
    
    6-在django项目根目录中,将
    AUTH\u USER\u MODEL='m\u USER.MUser'
    添加到setting.py

    我在我的项目中测试了它,它工作得非常好。我希望我没有错过任何东西。这样,招摇过市者也会在令牌参数中显示“email”而不是“username”: