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



我很抱歉,因为之前有人问过我这个问题(https://stackoverflow.com/a/5570717/3529404)。然而,我对用户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的值。

基本上,我只想使用上一个链接答案中的代码(https://stackoverflow.com/a/5570717/3529404),但强制要求新密码与旧密码不同。

谢谢!!

更新

正如评论中所讨论的,我认为我目前最沮丧的地方是不了解用户/实例的区别。特别是,当打印用户密码和实例密码时(见下文(,即使在每个密码中输入了相同的密码,哈希值也会不同。

我的代码与@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/medleware.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

在更改密码时,要求经过身份验证的用户提供当前密码通常是一种很好的做法。这防止了登录用户带着活动会话和一些"不可能"离开工作站的情况,即使这种情况不太可能;邪恶;用户试图通过更改密码来劫持他们的帐户。

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

更新

正如您所提到的,Django保存的是散列,而不是实际的密码,作为进一步的保护,密码是加盐的,请参阅Django文档中关于如何存储密码。因此,您将无法简单地比较哈希值。在更新User对象之前,您可以使用表单数据在表单的clean_new_password1方法中测试新旧密码是否匹配。这可以通过简单的比较或尝试使用warath编码器所描述的旧密码进行身份验证来完成。

我会这样做:

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

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

最新更新