You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

devise-security gem密码归档功能失效:旧密码重复检测异常求助

解决方案

问题根源

devise-security 默认依赖 BCrypt 处理旧密码的加密与验证,但你的系统使用的是 restful_authentication_sha1 加密器,导致:

  1. 旧密码被错误地用 BCrypt 加密存入 old_passwords 表
  2. 验证时用 BCrypt 逻辑解析 SHA1 哈希,抛出 InvalidHash 错误

步骤1:修复旧密码保存逻辑

在 User 模型中重写 save_password_history 方法,确保用当前系统的 SHA1 加密规则保存旧密码(包含 salt):

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :password_expirable, :password_archivable

  # 重写devise-security的旧密码保存方法
  def save_password_history
    return unless encrypted_password_changed? && encrypted_password.present?
    # 按当前加密规则保存密码与salt
    old_passwords.create!(encrypted_password: encrypted_password, password_salt: password_salt)
    # 超出存档数量限制的旧密码自动删除
    old_passwords.order(created_at: :desc).offset(self.class.password_archiving_count).destroy_all
  end
end

步骤2:重写旧密码验证逻辑

同样在 User 模型中重写 password_archive_included? 方法,用 SHA1 算法验证旧密码:

def password_archive_included?(password)
  old_passwords.exists? do |old_pw|
    # 还原restful_authentication_sha1的验证逻辑:SHA1(salt + 明文密码)
    generated_hash = Digest::SHA1.hexdigest("#{old_pw.password_salt}#{password}")
    generated_hash == old_pw.encrypted_password
  end
end

步骤3:清理无效的旧密码记录

之前错误存入的 BCrypt 格式哈希无法验证,需要批量删除:

# 在rails控制台执行,或者写成rake任务
OldPassword.where("encrypted_password LIKE '$2%'").destroy_all

(BCrypt 哈希通常以 $2a$/$2b$ 开头,SHA1 哈希是40位十六进制字符串,用此规则过滤无效记录)

步骤4:验证功能

  1. 修改用户密码,检查 old_passwords 表是否存入了 SHA1 格式的加密密码与对应 salt
  2. 尝试复用旧密码,确认系统会拦截并提示错误

内容的提问来源于stack exchange,提问作者Alejandra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:22:22