has_secure_password在密码更新时验证内部验证

时间:2012-09-30 17:55:47

标签: ruby-on-rails ruby-on-rails-3 activerecord mongoid ruby-on-rails-3.2

我在用户模型中使用has_secure_password。我已经为用户实现了一种在模型之外更改密码的方法,但是为了保持干燥,我正在尝试将控制器所需的验证移动到模型中。

用户模型如下所示:

class User
  include Mongoid::Document
  include ActiveModel::SecurePassword

  has_secure_password

  field: :password_digest, type: String

  attr_accessible :password, :password_confirmation, :current_password
end

用户通过提交以下内容来更改密码:

user[current_password] - Currently stored password
user[password] - New password
user[password_confirmation] - New password confirmation

我在当前用户的User模型上使用update_attributes(params [:user])。我的问题是调用update_attributes在使用验证之前更新了password_digest,因此以下代码将不起作用:

def password_validation_required?
  password_digest.blank? || !password.blank? || !password_confirmation.blank?
end

validate(on: :update, if: :password_validation_required?) do
  unless authenticate(current_password)
    add(:current_password, 'invalid password')
  end
end

身份验证是基于从用户[密码]生成的新密码_digest进行身份验证。是否有一种优雅的方式来访问旧的password_digest值进行身份验证?我的一个想法是重新查询用户以获得对将使用旧的password_digest值进行身份验证的另一个身份验证方法的访问权限。问题在于它不是一个干净的解决方案。

2 个答案:

答案 0 :(得分:1)

我认为这个比@ Parazuce的更清洁:

  validate :validates_current_password

  private

  def validates_current_password
    return if password_digest_was.nil? || !password_digest_changed?
    unless BCrypt::Password.new(password_digest_was) == current_password
      errors.add(:current_password, "is incorrect")
    end
  end

答案 1 :(得分:0)

password_digest字段有与之关联的ActiveModel :: Dirty方法,所以我决定选择:

validate(on: :update, if: :password_validation_required?) do
  unless BCrypt::Password.new(password_digest_was) == current_password
    errors.add(:current_password, "is incorrect")
  end
end

这可以防止需要使用额外的逻辑覆盖password=,如果使用其他功能password=,将来可能会引入错误。