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

如何结合lockbox与money-rails gem加密金额字段?

问题:lockbox与money-rails兼容方案探究

我有一个包含需加密金额字段的ActiveRecord模型,同时使用lockbox和money-rails gem分别实现字段加密与货币格式化,但二者无法直接兼容:

  • money-rails会将salary_cents和salary_currency字段对转换为salary虚拟属性,自动处理货币的格式化与分/元转换
  • lockbox会将ssn_ciphertext字段转换为ssn虚拟属性,自动处理加解密操作

尝试通过迁移添加salary_cents_ciphertext或salary_ciphertext_cents这类组合字段时,均触发以下错误:

irb(main):004:0> cs.salary= "$1.99"
/usr/local/bundle/gems/activemodel-7.0.8.4/lib/active_model/attribute.rb:211:in with_value_from_database': can't write unknown attribute salary_cents` (ActiveModel::MissingAttributeError)

raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
      ^^^^^

目前考虑放弃money-rails,改用普通加密整数字段手动处理解析与格式化,但希望找到兼容二者的方案,要求可靠性与开发体验优先。

可行兼容方案

方案1:手动映射money-rails到lockbox加密字段

核心思路是让money-rails操作lockbox提供的虚拟属性,而非直接操作数据库字段,步骤如下:

  1. 数据库迁移:添加加密后的分和货币字段,比如encrypted_salary_cents和encrypted_salary_currency(避开lockbox默认的_ciphertext后缀,避免命名冲突)
  2. 模型配置:
    class Employee < ApplicationRecord
      # 配置lockbox,自定义加密字段名,生成解密后的虚拟属性
      encrypts :salary_cents, :salary_currency, 
               key: :lockbox_key, 
               attribute: ->(name) { "encrypted_#{name}" }
      # 让money-rails使用lockbox生成的虚拟属性
      monetize :salary_cents, with_model_currency: :salary_currency
    end
    
    此方案通过自定义lockbox的加密字段命名规则,避免与money-rails的字段逻辑冲突,确保两个gem的虚拟属性链路通顺。

方案2:调整加载顺序,让lockbox优先初始化

修改gem加载顺序(在Gemfile中让lockbox排在money-rails之前),确保lockbox先生成salary_cents和salary_currency虚拟属性,再让money-rails基于这些属性工作:

  1. 数据库迁移:添加salary_cents_ciphertext和salary_currency_ciphertext字段(使用lockbox默认命名)
  2. Gemfile调整:
    gem 'lockbox'
    gem 'money-rails'
    
  3. 模型配置:
    class Employee < ApplicationRecord
      # 先初始化lockbox的加密虚拟属性
      encrypts :salary_cents, :salary_currency
      # 再让money-rails基于已存在的虚拟属性创建salary字段
      monetize :salary_cents, currency_column: :salary_currency
    end
    
    注:部分Rails版本可能存在加载顺序的隐性问题,需测试验证稳定性。

方案3:封装自定义金额虚拟属性(可靠性优先)

如果前两种方案存在隐性兼容问题,可采用折中方案:保留lockbox加密,手动封装money-rails核心功能,完全规避gem冲突:

  1. 数据库迁移:添加salary_cents_ciphertext和salary_currency_ciphertext字段,用lockbox加密这两个字段
  2. 模型封装:
    class Employee < ApplicationRecord
      encrypts :salary_cents, :salary_currency
    
      # 自定义salary虚拟属性,模拟money-rails的赋值与取值逻辑
      def salary
        return nil unless salary_cents.present? && salary_currency.present?
        Money.new(salary_cents, salary_currency)
      end
    
      def salary=(value)
        money = case value
                when String then Money.parse(value)
                when Money then value
                else raise ArgumentError, "Invalid salary format"
                end
        self.salary_cents = money.cents
        self.salary_currency = money.currency.iso_code
      end
    
      # 可选:添加格式化方法,贴近money-rails体验
      def formatted_salary
        salary&.format(decimal_mark: '.', thousands_separator: ',')
      end
    end
    
    此方案完全可控,无gem依赖冲突,开发体验接近money-rails,且可靠性最高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:42:16