【问题标题】:Rails 2 hook to modify data before it is read/written to MySQL DBRails 2钩子在数据被读/写到MySQL DB之前修改数据
【发布时间】:2016-03-13 17:50:00
【问题描述】:

我有一个我正在尝试修改的 rails 2 应用程序,以便在将属性写入 MySql DB 之前对其进行编码,并在读取时对其进行解码(不是所有属性,只是预先确定的属性)。

我查看了一些 gem,特别是 attr 加密的,但它并没有完全符合我的要求(我还试图避免重新命名我现有的任何表列,这似乎是 attr 的要求-加密)。

我在模型中添加了一个 before_save 过滤器,以便在将其保存到数据库之前进行属性修改,并且我已经覆盖了我的属性 getter 来进行解码。虽然这可行,但我想在堆栈中进行较低的解码(即在读取 DB 之后),以便让一切正常运行,而不需要在系统范围内进行更改(它还简化了决定何时编码/解码时的逻辑)。

所以这意味着我想做以下事情:

1) 在读取 DB 时,执行相反的操作,这样如果我执行 Model.last,我的属性值将是解码后的值(无需显式调用属性 getter)。

2) 覆盖 find_by_* 方法,以便通过我的编码属性进行搜索将首先对搜索词进行编码,然后使用该值进行 db 查询。

我该怎么做呢?

【问题讨论】:

  • 顺便说一句...在 Rails 2 应用程序上添加安全加密听起来像安全剧院。您确实需要更新应用程序,因为不支持 rails 2。

标签: mysql ruby-on-rails hook rails-activerecord ruby-on-rails-2


【解决方案1】:

更新:不幸的是,此方法在 Rails 2 中不起作用。自定义序列化程序可能已在 Rails 3 中添加。

原答案如下:

我认为您可以尝试使用this blog post 中所述的自定义serializer。这个特性即使在 Rails 2 中也应该存在(否则我猜这些 SO questions 不会存在)。

将属性编码为 Base64 的示例序列化程序:

# app/models/model.rb
class Model < ActiveRecord::Base
  serialize :my_attr, MyEncodingSerializer
end

# lib/my_encoding_serializer.rb
class MyEncodingSerializer
  require "base64"

  def self.load(value)
    # called when loading the value from DB
    value.present? ? Base64.decode64(value) : nil
  end

  def self.dump(value)
    # called when storing the value into DB
    value.present? ? Base64.encode64(value) : nil
  end
end

在 Rails 控制台中测试:

>> Model.create(my_attr: "my secret text")
D, [2016-03-14T07:17:26.493598 #14757] DEBUG -- :    (0.1ms)  BEGIN
D, [2016-03-14T07:17:26.494676 #14757] DEBUG -- :   SQL (0.6ms)  INSERT INTO `models` (`my_attr`) VALUES ('bXkgc2VjcmV0IHRleHQ=\n')
D, [2016-03-14T07:17:26.499356 #14757] DEBUG -- :    (4.4ms)  COMMIT
=> #<Model id: 4, my_attr: "my secret text">

您可以看到my_attr 值在保存到数据库之前自动编码。

从数据库加载当然也是透明的:

>> Model.last
D, [2016-03-14T07:19:01.414567 #14757] DEBUG -- :   Model Load (0.2ms)  SELECT  `models`.* FROM `models`  ORDER BY `models`.`id` DESC LIMIT 1
=> #<Model id: 4, my_attr: "my secret text">

所有查找器助手也应该工作,例如:

>> Model.find_by_my_attr("other text")
D, [2016-03-14T07:20:06.125670 #14757] DEBUG -- :   Model Load (0.3ms)  SELECT  `models`.* FROM `models` WHERE `models`.`my_attr` = 'b3RoZXIgdGV4dA==\n' LIMIT 1
=> nil  # nothing found here for wrong my_attr value

>> Model.find_by_my_attr("my secret text")
D, [2016-03-14T07:21:04.601898 #14757] DEBUG -- :   Model Load (0.6ms)  SELECT  `models`.* FROM `models` WHERE `models`.`my_attr` = 'bXkgc2VjcmV0IHRleHQ=\n' LIMIT 1
=> #<Model id: 4, my_attr: "my secret text">   # FOUND!

【讨论】:

  • 我试过了,但看起来 Rails 2 不支持自定义序列化程序(尽管我还没有看到关于这方面的具体文档)。如果我在 Rails 4 上尝试这种方法,它会起作用,如果我在 Rails 2 项目中做同样的事情,我会收到一个错误,“ActiveRecord::SerializationTypeMismatch: 属性应该是一个自定义编码器,但是是一个字符串”
  • 我明白了。在查看Rails 2 serialization source code 时,它的工作方式确实不同。在 Rails 2 中,类(serialize 的第二个参数)表示属性应该被反序列化的类。并且属性总是被序列化(存储在 db 中)作为 YAML。所以,你是对的,自定义序列化器在 Rails 2 中无法工作,抱歉。
【解决方案2】:

看起来 rails 2 有 'after_initialize' 回调,它应该可以得到你想要的东西(在一点性能上):

class Model < ActiveRecord::Base
  after_initialize do |model|
    # your decryption code here
  end
end

http://guides.rubyonrails.org/v2.3.11/activerecord_validations_callbacks.html#after-initialize-and-after-find

【讨论】:

  • 10dot,after_initialize 方法似乎有效。所以我所做的是在数据写入数据库之前使用 before_save 对数据进行编码,然后使用 after_initialize 对其进行解码。我现在遇到的问题是,当我检索记录然后修改它时,在内存中值被编码。我尝试添加一个 after_save 过滤器来进行解码,但是调用 self[:attr] = "decoded value" 会导致启动活动记录回滚......知道可能是什么原因造成的吗?
  • 我认为这是因为 after_save 仍在用于数据库更新的事务块内,因此更改将导致验证失败。尝试在更新返回后对您正在更新的任何活动记录对象调用 .reload() ......这应该会强制 after_initialize 回调再次触发(我认为)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-05
  • 1970-01-01
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
相关资源
最近更新 更多