Rails 7 has_secure_password更新空密码时未触发验证错误
Rails 7.0.4中has_secure_password更新空密码不触发验证的问题解析
问题场景
使用Rails 7.0.4,User模型代码如下:
class User < ApplicationRecord has_secure_password validates :fullname, presence: true, length: { maximum: 100 } validates :email, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP, message: "Please use a valid email address."}, uniqueness: true end
对已存在用户执行更新操作,设置password和password_confirmation为空值时,预期触发“can't be blank”验证错误,但实际无错误抛出,事务成功提交:
pry(main)> u = User.first User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]] => #<User:0x0000000108e89280...> [4] pry(main)> u.update(password: '', password_confirmation: '') TRANSACTION (0.2ms) BEGIN User Exists? (0.5ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 AND "users"."id" != $2 LIMIT $3 [["email", "foo@bar.com"], ["id", 1], ["LIMIT", 1]] TRANSACTION (0.2ms) COMMIT => true
直接设置属性调用save方法,结果一致。
原因分析
has_secure_password的默认验证逻辑是仅在创建用户时强制密码非空,这是Rails的设计默认——假设用户更新账号时,可能只修改fullname、email这类非密码字段,不需要重新输入密码。
执行更新操作时:
- 若未提供
password(或设为空字符串),has_secure_password会忽略密码字段的更新,不会触发必填验证 - 只有当
password_confirmation存在时,才会验证它和password是否匹配
因此设置空密码时,既不会触发必填验证,也不会更新数据库中的密码哈希字段。
解决方案
根据业务需求选择合适的验证规则:
1. 仅当修改密码时验证(推荐)
只有当用户主动修改密码时,才触发密码非空和长度验证,不影响其他字段的正常更新:
class User < ApplicationRecord has_secure_password validates :fullname, presence: true, length: { maximum: 100 } validates :email, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP, message: "Please use a valid email address."}, uniqueness: true # 当密码字段有变化时,强制验证非空和最小长度 validates :password, presence: true, length: { minimum: 6 }, if: :password_changed? end
添加后,执行u.update(password: '', password_confirmation: '')会触发“can't be blank”错误,事务不会提交。
2. 每次更新都强制验证密码(慎用)
如果业务要求用户每次更新账号都必须提供有效密码,可添加全局更新验证:
class User < ApplicationRecord has_secure_password validates :fullname, presence: true, length: { maximum: 100 } validates :email, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP, message: "Please use a valid email address."}, uniqueness: true # 每次更新操作都强制验证密码非空和最小长度 validates :password, presence: true, length: { minimum: 6 }, on: :update end
注意:这种方式会导致用户无法单独更新fullname或email,必须同时提供有效密码。
内容的提问来源于stack exchange,提问作者Nick Res
相关产品推荐
相关产品推荐

