has_secure_password密码更新时在验证内部进行身份验证



我在用户模型中使用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

我在当前用户的用户模型上使用update_attributes(参数[:用户])。我的问题是调用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

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

我认为这个比@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

password_digest 字段有与之关联的 ActiveModel::D irty 方法,所以我决定使用:

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=,则可能会引入错误。

最新更新