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

1. 简介

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

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

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

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

学习内容

在这个包含多个步骤的 Codelab 中,您将学习如何使用 Firebase Remote Config 为您的应用发布 App Check。

此 Codelab 使用的 Firebase 项目基于 DatabaseExample 快速入门应用构建而成,并与 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. 创建一个符合 AppCheckProvider 协议的 NSObject 子类文件“MyAppCheckProvider”。
  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 提供程序。

添加 Google Analytics(分析)事件

添加 Analytics 事件后,您可以更好地了解 App Check 的部署效果如何。Analytics(分析)将帮助确定是否应为更广泛的受众群体启用 App Attest。

记录两个 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 中创建的 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,请点击创建配置
  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 Testing 创建 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(分析)受众群体

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

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

可选:使用 Crashlytics 监控崩溃情况

如需在发布期间更好地了解应用的稳定性,请使用 Firebase Crashlytics 监控崩溃和非严重事件。

6. 恭喜!

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

其他资源: