Django - 重新访问“下次登录时更改密码”

Django - revisiting "change password at next login”

抱歉,之前有人问过这个问题 ()。但是,我对用户 Chris Pratt 接受的答案有疑问。该代码通常有效 - 我能够强制重置密码。但是,我遇到麻烦的地方是试图确保新密码与旧密码不同。由于代码当前编写,允许使用相同的密码。

来自 Chris 的回答:

def password_change_signal(sender, instance, **kwargs):
    try:
        user = User.objects.get(username=instance.username)
        if not user.password == instance.password:
          profile = user.get_profile()
          profile.force_password_change = False
          profile.save()
    except User.DoesNotExist:
        pass

似乎应该在以下行中检查:

if not user.password == instance.password:

但是,当我打印 user.password 和 instance.password(尽管在两个字段中输入相同的密码)时,散列值不相等。奇怪的是,如果我不断更改密码,为 instance.password 打印的值将在下一次更改时变为 user.password 的值。

基本上,我想做的就是使用之前链接答案中的代码 (),但强制要求新密码与旧密码不同。

谢谢!!

更新!

正如评论中所讨论的那样,我认为我目前主要的挫败感是不了解 user/instance 到底有何不同。特别是,当同时打印用户密码和实例密码时(见下文),即使在每个密码中输入了相同的密码,哈希值也不同。

我的代码与@Chris Pratt 的代码略有不同,因为我没有使用折旧的用户配置文件命令。希望我没有遗漏任何东西!

webapp/models.py

class UserAdditional(models.Model):
    user = models.ForeignKey(User, unique=True)
    force_password_change = models.BooleanField(default=True)

def create_user_additional_signal(sender, instance, created, **kwargs):
    if created:
        UserAdditional.objects.create(user=instance)

def password_change_signal(sender, instance, **kwargs):
    try:
        user = User.objects.get(username=instance.username)
        # these hashed values differ, even when the instance password entered is the same as the stored user password
        print user.password
        print instance.password
        if not user.password == instance.password:
            useradditional_obj = UserAdditional.objects.get(user=user)
            useradditional_obj.force_password_change = False
            useradditional_obj.save()
    except User.DoesNotExist:
        pass

signals.pre_save.connect(password_change_signal, sender=User, dispatch_uid='webapp.models')

signals.post_save.connect(create_user_additional_signal, sender=User, dispatch_uid='webapp.models')

webapp/middleware.py

class PasswordChangeMiddleware:
    def process_request(self, request):
        if request.user.is_authenticated() and not re.match(r'^/password_change/?', request.path) \
            and not re.match(r'^/logout/?', request.path):
            useradditional_obj = UserAdditional.objects.get(user=request.user)
            if useradditional_obj.force_password_change:
                return HttpResponseRedirect('/password_change/')

webapp/forms.py --- 用于强制执行密码要求

class ValidatingPasswordForm(object):
    MIN_LENGTH = 8

    def clean_new_password1(self):
        password1 = self.cleaned_data.get('new_password1')

        # At least MIN_LENGTH long
        if len(password1) < self.MIN_LENGTH:
            raise forms.ValidationError("The new password must be at least %d characters long." % self.MIN_LENGTH)

        # check numbers and special characters
        nums = len(set(re.findall(r'[0-9]',password1)))
        symb = len(set(re.findall(r'[~!@#$%^&\*()_+=-`]',password1)))

        if nums <= 0 or symb <= 0:
            raise forms.ValidationError("The new password must contain at least one number and one special character [~!@#$%^&\*()_+=-`]")

        return password1

class ValidatingPasswordChangeForm(ValidatingPasswordForm, auth.forms.PasswordChangeForm):
    pass

class ValidatingSetPasswordForm(ValidatingPasswordForm, auth.forms.SetPasswordForm):
    pass

我会这样做:

def password_change_signal(sender, instance, **kwargs):
try:
    user = authenticate(username=instance.username, password=instance.password)
    if user is None:  # means auth failed, which means password is not the same as the current password.
        user = User.objects.get(username=instance.username)
        user.set_password(instance.password)
        user.save()
        profile = user.get_profile()
        profile.force_password_change = False
        profile.save()
except User.DoesNotExist:
    pass

基本上我会尝试使用他们提供的密码对用户进行身份验证,如果新密码与当前密码不同,它应该会失败。

要求经过身份验证的用户在更改密码时提供当前密码通常是一种很好的做法。这可以防止这种情况发生,即使这种情况不太可能发生,即已登录的用户离开工作站时处于活动状态,并且一些“邪恶”的用户试图通过更改密码来劫持他们的帐户。

通过要求用户输入旧密码和新密码,您还可以防止在客户端和服务器端重复使用密码。这可以提高用户的可用性,因为您可以警告他们并禁止使用 JavaScript 提交表单。通过捕获旧密码和新密码,您可以将它们传递给服务器并验证是否重复使用,类似于 warath-coder 提供的答案。

更新

正如您提到的,Django 保存了哈希值而不是实际密码,并且作为进一步的保护,密码被加盐,请参阅 how passwords are stored 上的 Django 文档。因此,您将无法简单地比较哈希值。您可以在更新用户对象之前使用表单数据在表单的 clean_new_password1 方法中测试旧密码和新密码是否匹配。这可以通过简单的比较或尝试使用 warath-coder 描述的旧密码进行身份验证来完成。