跳至内容 跳至搜索
方法
F
S

实例公共方法

find_signed(signed_id, purpose: nil, on_rotation: nil)

允许您根据一个已签名的 ID 来查找记录,该 ID 是安全的,可以公开而不必担心被篡改。这对于密码重置或电子邮件验证等场景特别有用,您希望已签名的 ID 的持有者能够与底层记录进行交互,但通常仅限于特定时间段内。

您在生成时使用实例方法 signed_id(expires_in: 15.minutes) 来设置已签名 ID 的有效时间。如果在尝试查找已签名 ID 之前时间已过,则该已签名 ID 将不再有效,并将返回 nil。

可以使用 purpose(目的)来进一步限制已签名 ID 的使用。当您有一个通用的基础模型(如 User)时,这很有帮助,它可能拥有用于密码重置或电子邮件验证等多种用途的已签名 ID。生成时设置的 purpose 必须与查找时设置的 purpose 匹配。如果存在不匹配,将再次返回 nil。

示例

signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset

User.find_signed signed_id # => nil, since the purpose does not match

travel 16.minutes
User.find_signed signed_id, purpose: :password_reset # => nil, since the signed id has expired

travel_back
User.find_signed signed_id, purpose: :password_reset # => User.first
# File activerecord/lib/active_record/signed_id.rb, line 68
def find_signed(signed_id, purpose: nil, on_rotation: nil)
  raise UnknownPrimaryKey.new(self) if primary_key.nil?

  options = { on_rotation: on_rotation }.compact
  if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose), **options)
    find_by primary_key => id
  end
end

find_signed!(signed_id, purpose: nil, on_rotation: nil)

功能类似于 find_signed,但如果 signed_id 已过期、purpose 不匹配、是用于其他记录,或已被篡改,则会引发 ActiveSupport::MessageVerifier::InvalidSignature 异常。如果有效的已签名 ID 找不到记录,它还会引发 ActiveRecord::RecordNotFound 异常。

示例

User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature

signed_id = User.first.signed_id
User.first.destroy
User.find_signed! signed_id # => ActiveRecord::RecordNotFound
# File activerecord/lib/active_record/signed_id.rb, line 89
def find_signed!(signed_id, purpose: nil, on_rotation: nil)
  options = { on_rotation: on_rotation }.compact
  if id = signed_id_verifier.verify(signed_id, purpose: combine_signed_id_purposes(purpose), **options)
    find(id)
  end
end

signed_id_verifier()

# File activerecord/lib/active_record/signed_id.rb, line 96
def signed_id_verifier
  if signed_id_verifier_secret
    @signed_id_verifier ||= begin
      secret = signed_id_verifier_secret
      secret = secret.call if secret.respond_to?(:call)

      if secret.nil?
        raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed IDs"
      end

      ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON, url_safe: true
    end
  else
    return _signed_id_verifier if _signed_id_verifier

    if ActiveRecord.message_verifiers.nil?
      raise "You must set ActiveRecord.message_verifiers to use signed IDs"
    end

    ActiveRecord.message_verifiers["active_record/signed_id"]
  end
end

signed_id_verifier=(verifier)

允许您传递一个自定义的 verifier(验证器)来用于已签名 ID。这还允许您为不同的类使用不同的 verifier。如果您需要轮换密钥,这也会很有帮助,因为您可以提前为您的自定义 verifier 做好准备。有关详细信息,请参阅 ActiveSupport::MessageVerifier

# File activerecord/lib/active_record/signed_id.rb, line 122
def signed_id_verifier=(verifier)
  if signed_id_verifier_secret
    @signed_id_verifier = verifier
  else
    self._signed_id_verifier = verifier
  end
end