如何根据 django 中以前使用的密码检查密码

How to check password against previously used passwords in django

我有以下模型用于存储以前使用的散列密码:

class PasswordHistory(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    password = models.CharField(max_length=128, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now = True)

在更改密码表单中,我想检查用户要更改的新密码是否在过去 5 次没有被使用过。

这是我的表单验证:

class ProfileForm(forms.ModelForm):
    password1 = forms.CharField(widget=forms.PasswordInput(), required=False)
    password2 = forms.CharField(widget=forms.PasswordInput(), required=False)

    class Meta:
        model = Employee

        
    user_id = None
    
    def __init__(self, *args, **kwargs):
        self.user_id = kwargs.pop('user_id', None)
        super(ProfileForm, self).__init__(*args, **kwargs)
        
    def clean_password2(self):
        password1 = self.cleaned_data['password1']
        password2 = self.cleaned_data['password2']
        if password1 != password2:
            raise forms.ValidationError('Passwords do not match.')
        

        user = User.objects.get(pk=self.user_id)
        hashed_password = make_password(password1)
        password_histories = PasswordHistory.objects.filter(
        user=user,
        password_hashed_password
    )
    if password_histories.exists():
        raise forms.ValidationError('That password has already been used')        
    return password2

问题是密码每次都不一样,即使我一遍又一遍地尝试使用相同的明文密码。因此:

if password_histories.exists():

永远不会 returns 正确。

如果过去的密码由于加盐而总是不同,我如何比较过去的密码? 谢谢

.set_password 函数确实没有 return 任何东西,它只是 设置 密码。但是就像您说的那样,哈希是基于(随机)盐,因此每次哈希都会不同。因此,您应该使用 .check_password(…) function [Django-doc] 来验证它是否以某种方式匹配哈希变体:

from django.contrib.auth.hashers import <b>check_password</b>

class ProfileForm(forms.ModelForm):
    password1 = forms.CharField(widget=forms.PasswordInput(), required=False)
    password2 = forms.CharField(widget=forms.PasswordInput(), required=False)

    class Meta:
        model = Employee
    
    def __init__(self, *args, **kwargs):
        self.user_id = kwargs.pop('user_id', None)
        super(ProfileForm, self).__init__(*args, **kwargs)
        
    def clean_password2(self):
        password1 = self.cleaned_data['password1']
        password2 = self.cleaned_data['password2']
        if password1 != password2:
            raise forms.ValidationError('Passwords do not match.')
        user = User.objects.get(pk=self.user_id)
        password_histories = PasswordHistory.objects.filter(
            user=user
        )
        for pw in password_histories:
            if <b>check_password(password2, pw.password)</b>:
                raise forms.ValidationError('That password has already been used')
        return password2

因此,如果我们找到与给定的 原始 密码匹配的散列密码,我们可以 return password。如果在 for 循环结束时,我们没有找到任何这样的密码,我们可以 return password2,否则我们会引发错误。