为什么添加密码恢复后密码验证不起作用?
Why password validation does not work after adding password recovery?
遇到了一个我不清楚的问题。
我在注册时使用了密码验证。
def pass_val
if password_digest.count("a-z") <= 0 || password_digest.count("A-Z") <= 0
errors.add(:password, "must contain 1 small letter, 1 capital letter and minimum 8 symbols")
end
end
validates :password_digest, presence: true,
length: { minimum: 8 }
我可以为用户恢复密码,验证只是停止工作。
我将验证中的所有 password_digest
更改为 password
并且验证再次起作用。
但是,之后,密码恢复消失了,开始诅咒验证。
NoMethodError in PasswordResetsController#create
undefined method `count' for nil:NilClass
如果我再次将所有 password
更改为 password_digest
,验证将消失,但恢复密码将再次起作用。
更新
- 使用
password
- 在控制台中验证有效(在网站上也有效。密码恢复无效)。
- 使用
password_digest
- 控制台中的验证也有效(在网站上无效,恢复密码有效)。
更新 2
此外,在将信件发送到邮件(发送前)时,密码恢复过程中会出错,而不是在密码更改过程中。
更新 3
在 PasswordResetsController 中创建方法:
def create
author = Author.find_by_email(params[:email])
author.send_password_reset if author
flash[:success] = "Email sent with password reset instructions."
redirect_to root_path
end
更新 4
发送 password_reset:
def send_password_reset
confirmation_token
self.password_reset_sent_at = Time.zone.now
save!
AuthorMailer.password_reset(self).deliver!
end
但是信件没有发送,崩溃到此为止。
我想强调我要么在验证中使用password_digest
,但验证不起作用,密码恢复有效。或者我在验证和验证工作中使用 password
,但密码恢复没有。
另外值得注意的是,PasswordResetsController中的create方法并不是用来修改密码的,而是用来发邮件到邮箱的。搞不懂validation为什么骂他
错误在 send_password_reset
方法中。
向此方法添加了 (: validate => false)
,它起作用了。
def send_password_reset
confirmation_token
self.password_reset_sent_at = Time.zone.now
save!(:validate => false)
AuthorMailer.password_reset(self).deliver!
end
在验证中我使用 password
。
看来您自己已经找到了解决办法。但是我不确定你是否理解为什么会发生这种情况。
让我们从 password_digest
不包含密码而是密码的散列版本这一重要事实开始。 (散列函数的结果也称为摘要。)您需要将验证添加到 password
属性,因为它包含实际密码。
当您为作者分配新密码时,密码和密码摘要都将被分配一个新值。您可以想象 password
setter 看起来像这样:
def password=(password)
@password = password
@password_digest = do_some_hash_magic(password)
end
因为 password
是在您将它分配给作者时设置的,所以您可以验证它的内容。
另一件需要知道的重要事情是,实际密码永远不会保存到数据库中,只有密码摘要(出于安全原因)。出于这个原因,当您(重新)从数据库加载实例时 password
设置为 nil
,因为应用程序不再知道它是什么。
我建议使用 :allow_nil
option or making the validation conditional 而不是跳过 save(validate: false)
的验证,以便仅在 password
不是 nil
时触发验证。
validates :password, length: { in: 8..255 }, allow_nil: true
validates :password, format: { with: /\p{Lower}/, message: :no_lower }, allow_nil: true
validates :password, format: { with: /\p{Upper}/, message: :no_upper }, allow_nil: true
validates :password, format: { with: /\p{Digit}/, message: :no_digit }, allow_nil: true
validates :password, format: { with: /[\p{S}\p{P}]/, message: :no_special }, allow_nil: true
或者,您可以更明确地使用带有自定义上下文的 :on
选项,以仅在您希望它们触发时触发验证:
validates :password, length: { in: 8..255 }, on: :password
validates :password, format: { with: /\p{Lower}/, message: :no_lower }, on: :password
validates :password, format: { with: /\p{Upper}/, message: :no_upper }, on: :password
validates :password, format: { with: /\p{Digit}/, message: :no_digit }, on: :password
validates :password, format: { with: /[\p{S}\p{P}]/, message: :no_special }, on: :password
上下文不必以属性命名,使用 :foo
而不是 :password
也可以。然后,您可以通过在调用 valid?
或 save
.
时传递上下文来 运行 这些验证
author.valid?(:password)
author.save(context: :password)
如果您使用 Rails 5 或更高版本,并且想要 运行 混合验证,则可以将多个上下文作为数组传递。 author.valid?([:create, :password])
有关所用正则表达式的更多信息,请查看 Ruby regexp documentation。
遇到了一个我不清楚的问题。 我在注册时使用了密码验证。
def pass_val
if password_digest.count("a-z") <= 0 || password_digest.count("A-Z") <= 0
errors.add(:password, "must contain 1 small letter, 1 capital letter and minimum 8 symbols")
end
end
validates :password_digest, presence: true,
length: { minimum: 8 }
我可以为用户恢复密码,验证只是停止工作。
我将验证中的所有 password_digest
更改为 password
并且验证再次起作用。
但是,之后,密码恢复消失了,开始诅咒验证。
NoMethodError in PasswordResetsController#create
undefined method `count' for nil:NilClass
如果我再次将所有 password
更改为 password_digest
,验证将消失,但恢复密码将再次起作用。
更新
- 使用
password
- 在控制台中验证有效(在网站上也有效。密码恢复无效)。 - 使用
password_digest
- 控制台中的验证也有效(在网站上无效,恢复密码有效)。
更新 2 此外,在将信件发送到邮件(发送前)时,密码恢复过程中会出错,而不是在密码更改过程中。
更新 3 在 PasswordResetsController 中创建方法:
def create
author = Author.find_by_email(params[:email])
author.send_password_reset if author
flash[:success] = "Email sent with password reset instructions."
redirect_to root_path
end
更新 4 发送 password_reset:
def send_password_reset
confirmation_token
self.password_reset_sent_at = Time.zone.now
save!
AuthorMailer.password_reset(self).deliver!
end
但是信件没有发送,崩溃到此为止。
我想强调我要么在验证中使用password_digest
,但验证不起作用,密码恢复有效。或者我在验证和验证工作中使用 password
,但密码恢复没有。
另外值得注意的是,PasswordResetsController中的create方法并不是用来修改密码的,而是用来发邮件到邮箱的。搞不懂validation为什么骂他
错误在 send_password_reset
方法中。
向此方法添加了 (: validate => false)
,它起作用了。
def send_password_reset
confirmation_token
self.password_reset_sent_at = Time.zone.now
save!(:validate => false)
AuthorMailer.password_reset(self).deliver!
end
在验证中我使用 password
。
看来您自己已经找到了解决办法。但是我不确定你是否理解为什么会发生这种情况。
让我们从 password_digest
不包含密码而是密码的散列版本这一重要事实开始。 (散列函数的结果也称为摘要。)您需要将验证添加到 password
属性,因为它包含实际密码。
当您为作者分配新密码时,密码和密码摘要都将被分配一个新值。您可以想象 password
setter 看起来像这样:
def password=(password)
@password = password
@password_digest = do_some_hash_magic(password)
end
因为 password
是在您将它分配给作者时设置的,所以您可以验证它的内容。
另一件需要知道的重要事情是,实际密码永远不会保存到数据库中,只有密码摘要(出于安全原因)。出于这个原因,当您(重新)从数据库加载实例时 password
设置为 nil
,因为应用程序不再知道它是什么。
我建议使用 :allow_nil
option or making the validation conditional 而不是跳过 save(validate: false)
的验证,以便仅在 password
不是 nil
时触发验证。
validates :password, length: { in: 8..255 }, allow_nil: true
validates :password, format: { with: /\p{Lower}/, message: :no_lower }, allow_nil: true
validates :password, format: { with: /\p{Upper}/, message: :no_upper }, allow_nil: true
validates :password, format: { with: /\p{Digit}/, message: :no_digit }, allow_nil: true
validates :password, format: { with: /[\p{S}\p{P}]/, message: :no_special }, allow_nil: true
或者,您可以更明确地使用带有自定义上下文的 :on
选项,以仅在您希望它们触发时触发验证:
validates :password, length: { in: 8..255 }, on: :password
validates :password, format: { with: /\p{Lower}/, message: :no_lower }, on: :password
validates :password, format: { with: /\p{Upper}/, message: :no_upper }, on: :password
validates :password, format: { with: /\p{Digit}/, message: :no_digit }, on: :password
validates :password, format: { with: /[\p{S}\p{P}]/, message: :no_special }, on: :password
上下文不必以属性命名,使用 :foo
而不是 :password
也可以。然后,您可以通过在调用 valid?
或 save
.
author.valid?(:password)
author.save(context: :password)
如果您使用 Rails 5 或更高版本,并且想要 运行 混合验证,则可以将多个上下文作为数组传递。 author.valid?([:create, :password])
有关所用正则表达式的更多信息,请查看 Ruby regexp documentation。