使用 Django REST Framework 更新 USERNAME_FIELD 时如何处理令牌

How to handle token when UPDATE USERNAME_FIELD with Django REST Framework

我正在使用 Django REST Framework 和 Angular 4.
开发应用程序 我不知道更新登录用户信息时令牌的处理方式。

Django的用户模型定制如下
登录密钥从用户名更改为电子邮件。

[models.py]

class Account(AbstractBaseUser):
    username    = models.CharField(_('username'), max_length=30, unique=True)
    first_name  = models.CharField(_('first name'), max_length=30, blank=True)
    last_name   = models.CharField(_('last name'), max_length=30, blank=True)
    email       = models.EmailField(verbose_name='email address', max_length=255, unique=True)
    profile     = models.CharField(_('profile'), max_length=255, blank=True)
    is_active   = models.BooleanField(default=True)
    is_staff    = models.BooleanField(default=False)
    is_admin    = models.BooleanField(default=False)
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = AccountManager()
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def user_has_perm(user, perm, obj):
        return _user_has_perm(user, perm, obj)

    def has_perm(self, perm, obj=None):
        return _user_has_perm(self, perm, obj=obj)

    def has_module_perms(self, app_label):
        return self.is_admin

    def get_short_name(self):
        return self.first_name

    @property
    def is_superuser(self):
        return self.is_admin

    class Meta:
        db_table = 'api_user'
        swappable = 'AUTH_USER_MODEL'

Angular4更新用户信息的函数实现如下
使用函数 updateUserInfo 将新用户信息放入 Django REST Framework 中。

[component.ts]

updateUserInfo() {
  this.authService.updateUserInfo({
    email: this.editUserEmail,
    username: this.editUserName,
    profile: this.edtiUserProfile
  })
  .subscribe(
    data => {
      this.updateSuccessMessage = "success userinfo update";
      this.updateErrorMessage = null;
      this.authService.userInfo;
    },
    error => {
      this.updateErrorMessage = "failed userinfo update";
      this.updateSuccessMessage = null;
    }
  );
}

[service.ts]

updateUserInfo(userUpdateInfo) {
  return this.http
    .put(this.UpdateUserUrl,
      userUpdateInfo,
      this.jwt()
    );
}

jwt() {
  if (this.LoginToken) {
    let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'JWT ' + this.LoginToken.token });
    return new RequestOptions({ headers: headers });
  }
}

Django中更新用户信息的视图和序列化器如下

[views.py]

class AuthInfoUpdateView(generics.UpdateAPIView):
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = AccountSerializer
    lookup_field = 'email'
    queryset = Account.objects.all()

    def get_object(self):
        try:
            instance = self.queryset.get(email=self.request.user)
            return instance
        except Account.DoesNotExist:
            raise Http404

[serializers.py]

class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = Account
        fields = ('id', 'username', 'email', 'profile', 'password')

    def create(self, validated_data):
        return Account.objects.create_user(request_data=validated_data)

    def update(self, instance, validated_data):
        if 'password' in validated_data:
            instance.set_password(validated_data['password'])
        else:
            instance = super().update(instance, validated_data)
        instance.save()
        return instance

现在发生的问题是更新电子邮件时的行为。
因为USERNAME_FIELD是邮件,更新邮件就得更新前端持有的token。
但是我不知道如何用新邮件获取token。

我认为保持用户身份验证的唯一方法是生成一个新令牌并将其发回,我不知道使用 django-rest-framework-jwt 执行此操作的另一种方法。

为此,您需要自定义 AuthInfoUpdateViewput 方法:

def put(self, request, *args, **kwargs):
    serializer = self.serializer_class(self.get_object(), data=request.data, partial=True)

    if serializer.is_valid():
        instance = serializer.save()
        # Generate a new token
        payload = jwt_payload_handler(instance)
        token = jwt.encode(payload, settings.SECRET_KEY)
        response = JsonResponse({'token': token.decode('unicode_escape')})
        response.status = 200
        return response
    else:
        response = JsonResponse({'errors': serializer.errors})
        response.status = 500
        return response

就这么简单,您只需在前端更新令牌,而无需提示用户再次登录。

希望对您有所帮助!