如何根据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():

从不返回true。

如果过去的密码因盐而总是不同,该如何比较? 谢谢

解决方法

.set_password函数确实不返回任何内容,它只是设置密码。就像您说的那样,哈希是基于(随机)盐的,因此哈希每次都会不同。因此,您应该使用.check_password(…) function [Django-doc]来验证它是否与哈希变体匹配:

from django.contrib.auth.hashers import check_password

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 check_password(password2,pw.password):
                raise forms.ValidationError('That password has already been used')
        return password2

因此,如果我们找到与给定的 raw 密码匹配的哈希密码,则可以返回password。如果在for循环结束时未找到任何此类密码,则可以返回password2,否则会产生错误。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...