6

我的应用不需要密码,因为我想用手机和 OTP 登录。我正在尝试实现自定义的简单 JWT 令牌身份验证,它只需要一个电话号码,不需要密码。我是 Django 新手,我确实检查了 stackoverflow 中的一些链接并尝试了这个:

class CustomSerializer(TokenObtainPairSerializer):

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields[self.username_field] = serializers.CharField()
    del self.fields['password']



def validate(self,attr):
    print(attr)
    
    data = super().validate(attr)
    token = self.get_token(self.user)
    print (token)
    try:
        request = self.context["request"]
        print(request)
    except KeyError:
        pass
    request_data = json.loads(request.body)
    print(request_data)
 

所以这里当 validate 方法被执行时,它会去验证TokenObtainPairSerializerinit 方法,该方法会调用它的父类的 init 方法来验证密码。所以即使我在我的自定义序列化程序中删除密码字段,它仍然会给我一个密码错误。我试图传递密钥错误,但它再次在request.body.

我坚持这一点,我不知道如何在没有密码的情况下实现简单的 JWT。

4

1 回答 1

8

我有同样的问题,经过大量搜索和阅读 django-rest-framework-simplejwt的源代码后,我得到了答案。

所以即使我在我的自定义序列化程序中删除密码字段,它仍然给我密码的键错误

如果你看一下这个TokenObtainSerializer类,它是TokenObtainPairSerializer 这里的父序列化器,你可以看到密码是这样调用的:

# rest_framework_simplejwt/serializers.py

def validate(self, attrs):
    authenticate_kwargs = {
        self.username_field: attrs[self.username_field],
        'password': attrs['password'],
    }
    # ...

因此,即使您删除了密码字段,稍后仍会调用它。

我所做的是将密码字段设置为不需要并将空字符串分配为密码。

class TokenObtainPairWithoutPasswordSerializer(TokenObtainPairSerializer):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['password'].required = False

    def validate(self, attrs):
        attrs.update({'password': ''})
        return super(TokenObtainPairWithoutPasswordSerializer, self).validate(attrs)

现在可以在视图中使用这个序列化器。

class TokenObtainPairWithoutPasswordView(TokenViewBase):
    serializer_class = TokenObtainPairWithoutPasswordSerializer

然后我创建了一个自定义身份验证后端,以便用户无需密码即可进行身份验证。

from django.contrib.auth.backends import BaseBackend


class AuthenticationWithoutPassword(BaseBackend):

    def authenticate(self, request, username=None):
        if username is None:
            username = request.data.get('username', '')
        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

您可以阅读文档以获取有关创建自定义身份验证后端的更多信息。

最后,settings.py改变你的AUTHENTICATION_BACKENDS变量。

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'core.custom_authentication.AuthenticationWithoutPassword',
)

现在 Django 将尝试使用第一个身份验证ModelBackend,然后是新的AuthenticationWithoutPassword.

这里只是说显而易见的,但请记住,没有密码的身份验证绝对不安全,因此您应该为自定义身份验证添加更多逻辑,记住您可以访问该request变量。

于 2020-07-11T15:59:18.967 回答