如何在数据库迁移中正确使用attr_encrypted gem
问题分析与解决方案
旧数据未加密的原因及修复
迁移中直接通过user.email = user.o_email再save无法触发加密,核心原因是迁移执行时User模型的attr_encrypted配置未被正确加载,模型属性变更后ORM映射逻辑也未同步,导致加密回调不生效。
修复迁移代码
修改encrypt_data方法,手动在迁移中加载attr_encrypted配置,并强制触发加密逻辑(跳过验证避免干扰):
def encrypt_data # 确保User模型在迁移中加载attr_encrypted配置 User.class_eval do attr_encrypted :email, key: ENV['SECRET_KEY_BASE'].scan(/../).map { |x| x.hex }.pack('C*') end User.find_each do |user| user.email = user.o_email user.save!(validate: false) # 跳过验证,确保数据能被保存 end end
另一种更直接的方式是手动加密并写入字段,绕开模型回调:
def encrypt_data cipher = ActiveSupport::MessageEncryptor.new(ENV['SECRET_KEY_BASE'].scan(/../).map { |x| x.hex }.pack('C*')) User.find_each do |user| encrypted_value = cipher.encrypt_and_sign(user.o_email) # 拆分加密值和IV(attr_encrypted默认用--分隔加密值与IV) encrypted_email, iv = encrypted_value.split('--') user.update_columns( encrypted_email: encrypted_email, encrypted_email_iv: iv ) end end
重启控制台后无法读取user.email的原因及修复
该问题大概率是解密密钥不一致或环境变量未正确加载导致:
- 检查控制台重启后
ENV['SECRET_KEY_BASE']的值是否和创建用户时一致,可在控制台执行puts ENV['SECRET_KEY_BASE']确认。 - 确认密钥生成逻辑正确:
ENV['SECRET_KEY_BASE']需是符合长度要求的十六进制字符串(比如AES-256需要64位十六进制字符,对应32字节二进制密钥),转换后的二进制密钥需和attr_encrypted配置完全匹配。 - 若用dotenv管理环境变量,确保控制台启动时自动加载了.env文件(比如通过
rails c -e development启动,而非手动启动Ruby控制台)。
额外注意事项
- 迁移前务必备份数据,避免操作失误导致数据丢失。
- 迁移完成后可通过
User.first.encrypted_email对比新旧用户的加密字段格式,确认旧数据加密是否成功。
内容的提问来源于stack exchange,提问作者MZaragoza
相关产品推荐
相关产品推荐

