【问题标题】:How to prevent JWT Django (all-auth) from changing token when its username is updated?如何防止 JWT Django (all-auth) 在更新其用户名时更改令牌?
【发布时间】:2018-03-12 03:17:34
【问题描述】:

我正在使用 all-auth / django-rest-auth 进行授权。当用户更改他/她的用户名时,Django(Django REST Framework)会更改用户的令牌,这会使用户从应用程序中注销;如果其用户的令牌无效,我将应用程序设置为注销。

我想要做的是,即使用户更改了User 中的用户名、电子邮件或任何字段,它也会保留令牌。

这里是 settings.py

REST_USE_JWT = True
AUTHENTICATION_BACKENDS = (
    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    'allauth.account.auth_backends.AuthenticationBackend',

    # Facebook OAuth2
    'social_core.backends.facebook.FacebookAppOAuth2',
    'social_core.backends.facebook.FacebookOAuth2',

    # django-rest-framework-social-oauth2
    'rest_framework_social_oauth2.backends.DjangoOAuth2',
)
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=30),
}

...

谢谢!

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    建议:

    1) 将用户名设为哈希对象 (uuid) 并创建名为 username_custom 的新字段, (更改您的登录名和注册视图)

    2) 将您的模式准备为: USERNAME_FIELD = 'username' 所以它是 Django 中的默认用户名

    3) 永远不要更新默认字段用户名,您的令牌永远不会改变。

    示例

    1) 用户模型使用包:https://github.com/jcugat/django-custom-user

    2) 模型示例

    型号

    class UserModel(AbstractEmailUser):
        first_name = models.CharField(max_length=255)
        last_name = models.CharField(max_length=255)
    
        username = models.CharField(max_length=255, db_index=True, unique=True)
        email = models.EmailField(unique=False, db_index=True)
    
        username_custom = models.CharField(max_length=255, db_index=True, unique=True)
    
        USERNAME_FIELD = 'username'
    
        def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
            self.first_name = self.first_name.capitalize()
            self.last_name = self.last_name.capitalize()
            self.full_name = '{} {}'.format(self.first_name.capitalize(), self.last_name.capitalize())
            super(UserModel, self).save(force_insert, force_update)
    
        def clean(self):
            if UserModel.objects.filter(email=self.email, username_custom=self.username_custom).exists():
                raise ValidationError('User already exists!')
    

    您可以看到有新字段 username_custom

    现在休息 api 创建用户。

    class UserRegister(APIView):
        permission_classes = (AllowAny,)
        authentication_classes = ()
    
        def post(self, request):
            data = request.data
    
            # or you can check username_custom
            if UserModel.objects.filter(email=data['email']).exists():
                return Response(data={'success': False, 'msg': 'User with email already exists.'},
                                status=status.HTTP_403_FORBIDDEN)
    
            data['username'] = '%s' % uuid.uuid4()
    
            serializer = UserRegisterSerializer(data=data)
            if serializer.is_valid(raise_exception=True):
                serializer.save()
    
                token_data = UserModel.objects.get(email=serializer.data['email'])
                payload = jwt_payload_handler(token_data)
                token = jwt_encode_handler(payload)
    
                return Response(data={'success': True, 'user': serializer.data,
                                      'token': token_prefix + token}, status=status.HTTP_201_CREATED)
    
    
            return Response(data={'success': False, 'msg': serializer.errors},
                            status=status.HTTP_400_BAD_REQUEST)
    

    序列化器

    class UserRegisterSerializer(serializers.ModelSerializer):
        username = serializers.UUIDField()
    
        class Meta:
            model = UserModel
            fields = ('password', 'email', 'user_type', 'username', 'first_name', 'last_name', 'username_custom',)
    
        def create(self, validated_data):
            user = UserModel.objects.create_user(**validated_data)
    
            return user
    

    你必须准备 UPDATE 方法来检查唯一的电子邮件字段,这样你就不会在你的数据库中收到 2 封电子邮件,或者 username_custom 任何你想要的授权。

    登录接口

    class UserLoginView(APIView):
        permission_classes = (AllowAny,)
    
        def get(self, request, email, password):
    
            # or you can check username_custom
            if not UserModel.objects.filter(email=email.lower()).exists():
                return Response(data={'success': False, 'msg': 'Email or password wrong!'},
                                status=status.HTTP_404_NOT_FOUND)
    
            qv = UserModel.objects.get(email=email.lower())
            user = authenticate(username=qv.username, password=password)
    
            if user:
                if user.is_active:
                    token_data = UserModel.objects.get(id=user.id)
                    payload = jwt_payload_handler(token_data)
                    token = jwt_encode_handler(payload)
    
                    return Response(data={'success': True,
                                      'token': token_prefix + token}, status=status.HTTP_200_OK)
    
            return Response(data={'success': False, 'msg': 'Email or password wrong!'},
                                status=status.HTTP_400_BAD_REQUEST)
    

    【讨论】:

    • 我认为您的建议是非常好的方法...我们如何设置 USERNAME_FIELD = 'username'?你能有任何示例代码或教程吗?
    • 我会为你做的。
    • 非常感谢!!
    猜你喜欢
    • 2016-03-03
    • 2017-11-03
    • 2020-05-13
    • 2021-12-29
    • 2020-05-13
    • 1970-01-01
    • 2016-11-28
    • 2017-01-06
    • 2022-12-23
    相关资源
    最近更新 更多