使用 Firebase Remote Config 逐步部署 Firebase App Check

1. 简介

您可以将 Firebase App Check 与 App Attest 搭配使用,以保护您的后端服务,并验证对 Firebase 服务的请求是否来自您的真实应用。

通常,建议逐步引导用户开始使用 App Attest 服务,以免达到配额限制。如需了解详情,请参阅 Apple 的“准备使用 App Attest 服务”文档。

如果能够使用 Apple 的 App Store Connect 功能逐步发布应用更新(如“分阶段发布版本更新”中所述),可使 App Check 发布更加顺畅。这是一个简单直接的解决方案。但是,如果分阶段发布应用版本更新,那么您无法在不发布新应用版本的情况下控制现有更新应用的发布或更改行为。

要通过 App Attest 的发布更好地控制 App Check,一种方法是使用 Firebase Remote Config,一次为一定比例的应用用户启用 App Check 和 App Attest。这有助于避免来自证明服务器的限制。您可以使用 Google Analytics(分析)来观察分阶段发布对用户的影响。

学习内容

在此 Codelab 中,您将学习如何使用 Firebase Remote Config 为应用发布 App Check。

此 Codelab 使用基于 DatabaseExample 快速入门应用的 Firebase 项目,并已与 Firebase App Check 集成,如适用于 Apple 平台的 Firebase App Check Codelab 中所述。DatabaseExample 快速入门应用允许用户使用 Firebase Realtime Database 的功能登录并添加帖子。

您还可以调整此 Codelab 中的步骤,以测试您自己的应用。

前提条件

所需条件

  • Xcode 12.5 及更高版本
  • 对于 App Attest 测试:
    • 一个允许您创建新的应用标识符的 Apple 开发者帐号
    • 具有明确应用 ID 且已启用 App Attest 功能的应用。如果您需要与流程相关的帮助,请参阅注册应用 ID启用应用功能这两篇文章。
    • 支持 App Attest 的 iOS/iPadOS 设备
  • 具有以下各项的 Firebase 项目:
  • 访问您应用的关联 Firebase 项目,有权创建和管理 Remote Config 以及查看 Google Analytics(分析)

2. 创建自定义证明提供方

在此步骤中,我们将创建一个自定义提供程序类,以仅在启用 App Attest 时提供令牌。Remote Config 依赖于已配置的 Firebase 应用实例,而您在此步骤中实现的自定义提供程序将充当占位符来完成配置。

如需完成以下步骤,您需要在 Xcode 中应用的“框架、库和嵌入式内容”部分添加 FirebaseFirebaseRemoteConfigFirebaseAnalytics。如需查看具体操作方法的示例,请参阅“适用于 Apple 平台的 Firebase App Check”Codelab

  1. 创建一个文件“MyAppCheckProvider”,它是 NSObject 的子类,符合 AppCheckProvider 协议。
  2. 添加一个稍后将填写的空 getToken() 方法。

如需了解 getToken() 方法为空的自定义提供程序类,请参阅以下示例代码。

// MyAppCheckProvider.swift

import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig

class MyAppCheckProvider: NSObject, AppCheckProvider {
  func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {}
}

如需实例化 AppAttestProvider,您需要传递相应 FirebaseApp 的实例。为其创建一个存储属性,并接受它作为初始化程序参数:

// MyAppCheckProvider.swift

import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig

class MyAppCheckProvider: NSObject, AppCheckProvider {
  // Firebase app instance served by the provider.
  let firebaseApp: FirebaseApp

  // The App Check provider factory should pass the FirebaseApp instance.
  init(app: FirebaseApp) {
    self.firebaseApp = app
    super.init()
  }

  func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {}
}

将令牌请求转发给 App Attest 提供方

现在,您可以在 getToken() 方法中将令牌请求转发到 App Attest 提供方。

注意:如需详细了解 getToken() 方法,请参阅 FirebaseAppCheck 框架参考

将以下代码添加到 getToken() 方法中:

// MyAppCheckProvider.swift

import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig

class MyAppCheckProvider: NSObject, AppCheckProvider {
  // Firebase app instance served by the provider.
  let firebaseApp: FirebaseApp

  // The App Check provider factory should pass the FirebaseApp instance.
  init(app: FirebaseApp) {
    self.firebaseApp = app
    super.init()
  }

  private lazy var appAttestProvider = AppAttestProvider(app: firebaseApp)

  func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {
    // Fetch App Attest flag from Remote Config
    let remoteConfig = RemoteConfig.remoteConfig(app: firebaseApp)
    remoteConfig.fetchAndActivate { remoteConfigStatus, error in
      // Get App Attest flag value
      let appAttestEnabled = remoteConfig.configValue(forKey: "AppAttestEnabled").boolValue

      guard appAttestEnabled else {
        // Skip attestation if App Attest is disabled. Another attestation
        // method like DeviceCheck may be used instead of just skipping.
        handler(nil, MyProviderError.appAttestIsDisabled)
        return
      }

      // Try to obtain an App Attest provider instance and fail if cannot
      guard let appAttestProvider = self.appAttestProvider else {
        handler(nil, MyProviderError.appAttestIsUnavailable)
        return
      }

      // If App Attest is enabled for the app instance, then forward the
      // Firebase App Check token request to the App Attest provider
      appAttestProvider.getToken(completion: handler)
    }
  }
}

enum MyProviderError: Error {
  case appAttestIsDisabled
  case appAttestIsUnavailable
  case unexpected(code: Int)
}

上述代码会检查 Remote Config AppAttestEnabled 布尔值参数(此 Remote Config 参数将在稍后的 Codelab 中创建)。如果值为 false,则代码会失败,这表示 App Check 未在当前设备上发布。如果该值为 true,则代码会尝试获取 App Attest 提供程序,如果无法获取,则会失败。如果通过了这些错误检查,代码会将令牌请求转发给 App Attest 提供程序。

添加 Analytics 事件

通过添加 Analytics 事件,您可以更好地了解 App Check 发布的成功情况。Analytics 将帮助确定是否应面向更广泛的受众群体启用 App Attest。

记录两个 Google Analytics(分析)事件:成功时记录 AppAttestSuccess,失败时记录 AppAttestFailure。这两个 Analytics 事件有助于您跟踪 App Check 的发布成功情况,以及决定是否应该继续进行更大的发布。

func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {
  // Fetch Remote Config.
  let remoteConfig = RemoteConfig.remoteConfig(app: firebaseApp)
  remoteConfig.fetchAndActivate { remoteConfigStatus, error in
    // Get App Attest flag value from Remote Config.
    let appAttestEnabled = remoteConfig.configValue(forKey: "AppAttestEnabled").boolValue

    guard appAttestEnabled else {
      // Skip attestation if App Attest is disabled. Another attestation
      // method like DeviceCheck may be used instead of just skipping.
      handler(nil, MyProviderError.appAttestIsDisabled)
      return
    }

    // Try to obtain an App Attest provider instance and fail otherwise.
    guard let appAttestProvider = self.appAttestProvider else {
      handler(nil, MyProviderError.appAttestIsUnavailable)
      return
    }

    // If App Attest is enabled for the app instance, then forward the
    // Firebase App Check token request to the App Attest provider.
    appAttestProvider.getToken { token, error in
      // Log an Analytics event to track attestation success rate.
      let appAttestEvent: String
      if (token != nil && error == nil) {
        appAttestEvent = "AppAttestSuccess"
      } else {
        appAttestEvent = "AppAttestFailure"
      }
      Analytics.logEvent(appAttestEvent, parameters: nil)

      // Pass the result to the handler
      handler(token, error)
    }
  }
}

3. 更新提供程序工厂类

实现了将令牌请求转发到 App Attest 提供程序的逻辑并添加一些 Analytics 事件后,您需要更新在“适用于 Apple 平台的 App Check”Codelab 中创建的 MyAppCheckProviderFactory.class。该类将定位模拟器的 App Check 调试提供程序,否则定位您的自定义提供程序。

在您于“适用于 Apple 平台的 Firebase App Check”Codelab 中创建的 MyAppCheckProviderFactory 类中,修改以下代码:

// MyAppCheckProviderFactory.swift

import Firebase

class MyAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
  func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
      #if targetEnvironment(simulator)
      // App Attest is not available on simulators.
      // Use a debug provider.
      let provider = AppCheckDebugProvider(app: app)

      // Print only locally generated token to avoid a valid token leak on CI.
      print("Firebase App Check debug token: \(provider?.localDebugToken() ?? "" )")

      return provider
      #else
      if #available(iOS 14.0, *) {
        // Use your custom App Attest provider on real devices.
        return MyAppCheckProvider(app: app)
      } else {
        return DeviceCheckProvider(app: app)
      }
      #endif
  }
}

确认在配置 FirebaseApp 之前已设置 AppCheckProviderFactory

// DatabaseExampleApp.swift

import SwiftUI
import Firebase
import FirebaseAppCheck

@main
struct DatabaseExampleApp: App {
  init() {
    AppCheck.setAppCheckProviderFactory(MyAppCheckProviderFactory())
    FirebaseApp.configure()
  }

  // ...
}

4.在 Firebase 控制台中添加 Remote Config 参数

现在,您要将 Remote Config 参数 AppAttestEnabled 添加到 Firebase 控制台。您的 getToken 方法需要此参数。

如需在 Firebase 控制台中创建 Remote Config 参数,请执行以下操作:

  1. 为您的项目打开 Remote Config,然后点击添加参数。如果这是您第一次使用 Remote Config,请点击 Create configuration
  2. 参数名称(键)字段中,输入 AppAttestEnabled
  3. 数据类型下拉菜单中,选择布尔值
  4. 默认值下拉菜单中,选择 false

在 Firebase 控制台中创建 Remote Config 参数

在点击“保存”之前,为 10% 的用户创建一个条件值:

  1. 依次点击新增 > 条件值 > 创建新条件
  2. 名称字段中,输入条件名称。
  3. 适用条件...下,选择用户(随机百分位)<=,然后在 % 字段中输入 10
  4. 点击创建条件

在 Firebase 控制台中定义 Remote Config 条件

将条件值设置为 true,以便 App Attest 向 10% 的用户发布。

  1. 将您刚刚创建的条件的值设置为 true
  2. 点击保存

在 Firebase 控制台中查看 Remote Config 参数

完成后,发布 Remote Config 更改。

在您的设备上测试发布

如需在不修改应用代码的情况下在设备上测试不同的 Remote Config 标志值,请按照教程:使用 A/B 测试创建 Firebase Remote Config 实验,在 AppAttestEnabled 参数上配置实验。在测试设备上验证您的实验教程部分介绍了如何为测试设备分配不同的值。

最后一步是使用 Google Analytics(分析)监控 App Attest 发布的成功情况。

5. 查看 AppCheck 部署的成效

您可以在 Analytics 事件信息中心衡量发布的成功与否。留意 AppAttestSuccessAppAttestFailure 事件。事件最长可能需要 24 小时才会显示在信息中心内。或者,您也可以启用调试功能,并使用 DebugView 更快速地查看调试事件。

(可选)您可以在 Crashlytics 信息中心内监控崩溃率的增长情况。如需详细了解如何将 Crashlytics 添加到您的应用,请参阅 Firebase Crashlytics 使用入门

如果您看到的大部分是 AppAttestSuccess 事件,而少量 AppAttestFailure 事件,那么建议您修改 Remote Config 参数 AppAttestEnabled 中的条件,以增加启用 App Attest 的用户所占的百分比。

在 Firebase 控制台中查看 Analytics 事件

可选:利用 Google Analytics(分析)受众群体

如果您想进一步利用 Analytics 事件 AppAttestEnabled,可以创建 Analytics 受众群体,并通过将 AppAttestEnabled 设为 true 来跟踪用户。

App Attest 随 iOS 14.0 一起发布。您的部分用户可能不是此版本,因此不符合 App Attest 的条件。您可以记录另一个 Analytics 事件来跟踪这些用户,然后针对该受众群体使用其他证明方法(例如 DeviceCheck)。

可选:使用 Crashlytics 监控崩溃

为了更好地了解您的应用在发布期间的稳定性,请使用 Firebase Crashlytics 监控崩溃和非严重错误。

6. 恭喜!

您已成功使用 Remote Config 发布了 App Check 🎉?

其他资源: