在WHERE
子句中,有没有一种方法可以修改模型中的值,然后再使用MySQL?
具体来说,我正在使用电话号码,并且为了确保用户隐私,我只在数据库中存储SHA256哈希电话号码。 但是,我希望能够对模型进行处理,就好像它没有被散列一样。
例如:
def phone_number=(number)
write_attribute(:phone_number, OpenSSL::HMAC.hexdigest('SHA256', 'salt', number))
end
这使我可以保存原始电话号码并自动将其散列。但是有办法吗
PhoneNumbers.where(phone_number: '555-1234')
在将'555-1234'
转换为SQL之前是否对其进行了哈希处理?
答案 0 :(得分:3)
选中此https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html,尤其是“创建自定义类型”标题。
您定义一个自定义类型,并将其设置为属性的类型。您定义了一个“序列化”方法,该方法会将您的值转换为使用SQL语句。
从文档中
class Money < Struct.new(:amount, :currency)
end
class MoneyType < Type::Value
def initialize(currency_converter:)
@currency_converter = currency_converter
end
# value will be the result of +deserialize+ or
# +cast+. Assumed to be an instance of +Money+ in
# this case.
def serialize(value)
value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
value_in_bitcoins.amount
end
end
# config/initializers/types.rb
ActiveRecord::Type.register(:money, MoneyType)
# app/models/product.rb
class Product < ActiveRecord::Base
currency_converter = ConversionRatesFromTheInternet.new
attribute :price_in_bitcoins, :money, currency_converter: currency_converter
end
Product.where(price_in_bitcoins: Money.new(5, "USD"))
# => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
Product.where(price_in_bitcoins: Money.new(5, "GBP"))
# => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
如您所见,where方法接收Money对象,但是SQL语句具有十进制值。
答案 1 :(得分:0)
一个定制的序列化程序完美地解决了我的用例!
# lib/serializers/phone_hash_serializer.rb
class PhoneHashSerializer
def self.load(value)
value.to_s
end
def self.dump(value)
value = value.to_s
if value.start_with? 'hashed!'
value
else
# Rails serializer's do not handle one-way hashing well, as the value ends up getting
# getting dumped/hashed twice (once on the type-cast, then again before storing to DB).
# So, we "tag" the value as "hashed!" to ensure we don't double hash since search's
# only get hashed once, and would therefore never match the DB value
'hashed!' + hash_string(value)
end
end
private
def self.hash_string(value)
OpenSSL::HMAC.hexdigest('SHA256', 'hash_salt', value)
end
end
# models/phone_number.rb
require 'serializers/phone_hash_serializer'
...
serialize :phone_number, PhoneHashSerializer
...
我以http://ruby-journal.com/how-to-write-custom-serializer-for-activerecord-number-serialize/作为缪斯。感谢那些帮助过的人!