跳至内容 跳至搜索

这是包含在 ActiveRecord 模型中使其可加密的 Concern。它添加了 encrypts 属性声明,以及用于加密和解密记录的 API。

方法
A
C
D
E
G
O
P
S
V

常量

ORIGINAL_ATTRIBUTE_PREFIX = "original_"
 

实例公共方法

add_length_validation_for_encrypted_columns()

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 132
def add_length_validation_for_encrypted_columns
  encrypted_attributes&.each do |attribute_name|
    validate_column_size attribute_name
  end
end

ciphertext_for(attribute_name)

返回 attribute_name 的密文。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 157
def ciphertext_for(attribute_name)
  if encrypted_attribute?(attribute_name)
    read_attribute_before_type_cast(attribute_name)
  else
    read_attribute_for_database(attribute_name)
  end
end

decrypt()

解密所有可加密的属性并保存更改。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 171
def decrypt
  decrypt_attributes if has_encrypted_attributes?
end

deterministic_encrypted_attributes()

返回模型类中确定性加密属性的列表。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 58
def deterministic_encrypted_attributes
  @deterministic_encrypted_attributes ||= encrypted_attributes&.find_all do |attribute_name|
    type_for_attribute(attribute_name).deterministic?
  end
end

encrypt()

加密所有可加密的属性并保存更改。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 166
def encrypt
  encrypt_attributes if has_encrypted_attributes?
end

encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 84
def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)
  encrypted_attributes << name.to_sym

  decorate_attributes([name]) do |name, cast_type|
    scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
      downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties

    ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
  end

  preserve_original_encrypted(name) if ignore_case
  ActiveRecord::Encryption.encrypted_attribute_was_declared(self, name)
end

encrypted_attribute?(attribute_name)

返回给定属性是否已加密。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 146
def encrypted_attribute?(attribute_name)
  name = attribute_name.to_s
  name = self.class.attribute_aliases[name] || name

  return false unless self.class.encrypted_attributes&.include? name.to_sym

  type = type_for_attribute(name)
  type.encrypted? read_attribute_before_type_cast(name)
end

encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)

加密 name 属性。

Options

  • :key_provider - 用于提供加密和解密密钥的密钥提供程序。默认为 ActiveRecord::Encryption.key_provider

  • :key - 用于派生密钥的密码。它是提供派生密钥的 :key_provider 的简写。这两个选项不能同时使用。

  • :deterministic - 默认情况下,加密不是确定性的。它将在每次加密操作中使用随机的初始化向量。这意味着使用相同的密钥两次加密相同的内容将生成不同的密文。当设置为 true 时,它将根据加密内容生成初始化向量。这意味着相同的内容将生成相同的密文。这使得可以使用 Active Record 查询加密文本。确定性加密默认使用最旧的加密方案来加密新数据。您可以通过设置 deterministic: { fixed: false } 来更改此行为。这将使其使用最新的加密方案来加密新数据。

  • :support_unencrypted_data - 为 true 时,可以正常读取未加密的数据。为 false 时,将引发错误。如果未提供值,则回退到 config.active_record.encryption.support_unencrypted_data。这对于加密一个列但想禁用对未加密数据的支持而不必调整全局设置的场景很有用。

  • :downcase - 为 true 时,会自动将加密内容转换为小写。这允许在查询数据时有效地忽略大小写。请注意,大小写会丢失。如果您希望保留大小写,请使用 :ignore_case

  • :ignore_case - 为 true 时,它的行为类似于 :downcase,但它也会将原始大小写保存在一个特殊指定的列 +original_<name>+ 中。读取加密内容时,将提供带有原始大小写的版本。但您仍然可以执行忽略大小写的查询。此选项只能在 :deterministic 为 true 时使用。

  • :context_properties - 在加密和解密此属性时将覆盖 Context 设置的其他属性。例如:encryptor:, cipher:, message_serializer: 等。

  • :previous - 以前的加密方案列表。当提供时,在尝试读取属性时将按顺序使用它们。列表中的每个条目都可以包含 encrypts 支持的属性。此外,当使用确定性加密时,它们将用于生成额外的密文以在查询中进行检查。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 49
def encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)
  self.encrypted_attributes ||= Set.new # not using :default because the instance would be shared across classes

  names.each do |name|
    encrypt_attribute name, key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties
  end
end

global_previous_schemes_for(scheme)

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 78
def global_previous_schemes_for(scheme)
  ActiveRecord::Encryption.config.previous_schemes.filter_map do |previous_scheme|
    scheme.merge(previous_scheme) if scheme.compatible_with?(previous_scheme)
  end
end

override_accessors_to_preserve_original(name, original_attribute_name)

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 109
def override_accessors_to_preserve_original(name, original_attribute_name)
  include(Module.new do
    define_method name do
      if ((value = super()) && encrypted_attribute?(name)) || !ActiveRecord::Encryption.config.support_unencrypted_data
        send(original_attribute_name)
      else
        value
      end
    end

    define_method "#{name}=" do |value|
      self.send "#{original_attribute_name}=", value
      super(value)
    end
  end)
end

preserve_original_encrypted(name)

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 98
def preserve_original_encrypted(name)
  original_attribute_name = "#{ORIGINAL_ATTRIBUTE_PREFIX}#{name}".to_sym

  if !ActiveRecord::Encryption.config.support_unencrypted_data && !column_names.include?(original_attribute_name.to_s)
    raise Errors::Configuration, "To use :ignore_case for '#{name}' you must create an additional column named '#{original_attribute_name}'"
  end

  encrypts original_attribute_name
  override_accessors_to_preserve_original name, original_attribute_name
end

scheme_for(key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 70
def scheme_for(key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
  ActiveRecord::Encryption::Scheme.new(key_provider: key_provider, key: key, deterministic: deterministic,
    support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, **context_properties).tap do |scheme|
    scheme.previous_schemes = global_previous_schemes_for(scheme) +
    Array.wrap(previous).collect { |scheme_config| ActiveRecord::Encryption::Scheme.new(**scheme_config) }
  end
end

source_attribute_from_preserved_attribute(attribute_name)

给定一个属性名,它返回原始属性的源属性名。

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 65
def source_attribute_from_preserved_attribute(attribute_name)
  attribute_name.to_s.sub(ORIGINAL_ATTRIBUTE_PREFIX, "") if attribute_name.start_with?(ORIGINAL_ATTRIBUTE_PREFIX)
end

validate_column_size(attribute_name)

# File activerecord/lib/active_record/encryption/encryptable_record.rb, line 138
def validate_column_size(attribute_name)
  if limit = columns_hash[attribute_name.to_s]&.limit
    validates_length_of attribute_name, maximum: limit
  end
end