我很抱歉,因为之前有人问过我这个问题(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
基本上,我尝试使用他们提供的密码对用户进行身份验证,如果新密码与当前密码不同,则会失败。