Rails has_secure_password 保存成功但随后给出 nil 密码 - 验证失败

Rails has_secure_password saves successfully but then gives nil password - validation fails

我有一个带有 has_secure_passwordvalidates :password, presence: true 的用户模型。

然后会发生以下情况:

$ rails console

> u = User.create(name: 'user', password: 'password')
=> #<User id: ...
> u.password
=> 'password'
> u.save
=> true
> exit

然后我再次 运行 控制台然后..

$ rails console
> u = User.find_by(name: 'user')
=> #<User id: ...
> u.password
=> nil
> u.valid?
=> false

我不明白为什么密码在第一次成功后就变成了空。 在开发应用程序中也会发生同样的情况,我可以使用 omniauth_google 登录用户,使用 SecureRandom.hex(15) 创建密码。当我尝试注销并通过 omniauth 重新登录同一用户时,找到了该用户,但 user.valid? 给出了 false,因为 :passowrd 神秘地变成了 nil,影响了登录。

这是用户的代码,也许我在这里有什么问题?

class User < ApplicationRecord
    has_many :notes
    has_many :goings
    has_many :cafes, through: :goings

    has_secure_password
    has_secure_password validations: false

    validates_uniqueness_of :name, {message: "%{value} name already exist"}
    validates :email, presence: { message: "%{attribute} must be given" }
  validates :password, presence: {
    message: 'You must enter a password'},
        length: {minimum: 2,
        message: 'Your password must contain at least 2 characters'
    }
    validates :name, presence: { message: "%{attribute} must be given" }

    def self.from_omniauth(response)
        User.find_or_create_by(uid: response[:uid], provider: response[:provider]) do |u|
            u.name = response[:info][:name]
            u.email = response[:info][:email]
            u.password = SecureRandom.hex(15)
        end
    end
end

诀窍在于,对于 has_secure_passwordpassword 并不真正作为持久模型属性存在。它只是为了处理加密的原始密码而存在;之后它一直是 password_digest(因此为什么 .password returns nil 在现有对象上。

Rails 本身 'cheats' 在验证中,仅验证 password_digest (GitHub):

的存在和长度
# This ensures the model has a password by checking whether the password_digest
# is present, so that this works with both new and existing records. However,
# when there is an error, the message is added to the password attribute instead
# so that the error message will make sense to the end-user.
validate do |record|
  record.errors.add(:password, :blank) unless record.password_digest.present?
end

validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED

当有 clear-text 密码需要验证时,您的自定义验证在初始创建时工作正常。但在那之后,没有一个,所以你的存在检查失败(并且你的其他检查无关紧要)。要解决此问题,您可以将 on: create 添加到您的密码验证中,以便在新记录的情况下它们仅 运行。