将 reCAPTCHA 与 iOS 应用集成

本页介绍了如何在 iOS 应用中集成 reCAPTCHA。

由于移动设备在屏幕尺寸、性能以及应用界面方面存在差异,视觉复选框 reCAPTCHA 验证(我不是机器人)不适用于 iOS 移动应用。您可以改为实施自己的分级强制执行策略,例如 MFA 流程,为可疑流量提供替代兑换途径。

该 SDK 利用反射和动态代码来更新和改进已部署的应用或 SDK 中的检测系统。为避免干扰应用,系统中可用的类集会限制为经过仔细控制的列表。

准备工作

  1. 将应用的最低 SDK 设置为 iOS 12,或创建一个新的移动应用。

  2. 为 reCAPTCHA 准备环境

  3. 为 iOS 应用平台创建 reCAPTCHA 密钥

    或者,通过执行以下步骤之一,复制现有 reCAPTCHA iOS 版密钥的 ID:

    • 如需从 Google Cloud 控制台复制现有密钥的 ID,请执行以下操作:

      1. 前往 reCAPTCHA 页面。

        前往 reCAPTCHA

      2. 在 reCAPTCHA 密钥列表中,将鼠标指针悬停在要复制的密钥上,然后点击
    • 如需使用 REST API 复制现有密钥的 ID,请使用 projects.keys.list 方法。
    • 如需使用 gcloud CLI 复制现有密钥的 ID,请使用 gcloud recaptcha keys list 命令。

  4. 拥有 GitHub 账号。

  5. 阅读 Apple 隐私权详细信息

准备 iOS 环境

如需准备开发环境,请执行以下操作:

  1. 下载并安装最新版本的 Xcode,并创建一个新的空白 iOS 单视图应用

  2. 使用以下任一方法下载 SDK:

    CocoaPods

    1. 下载并安装 CocoaPods
    2. 创建一个 Podfile,并将以下几行代码添加到您的 Podfile:

      source "https://github.com/CocoaPods/Specs.git"
      
      target 'AppTarget' do
      
        # Podfiles must include use_frameworks! or
        # use_frameworks! :linkage => :static
        use_frameworks!
      
        pod "RecaptchaEnterprise", "18.8.0-beta01"
        ...
      
      end
      
    3. 运行 pod update 以安装所需的依赖项。

    Swift Package Manager

    1. 在 Xcode 中,依次选择 File > Add Packages,然后在搜索输入软件包网址字段中输入以下网址: https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk
    2. Xcode 对话框中,输入以下详细信息:

      • GitHub 用户名。
      • 您按照 GitHub 的说明创建的个人访问令牌。个人访问令牌必须具有 XCode 登录对话框中列出的范围。

      Xcode 会安装 SDK 及其所需的依赖项。

    Flutter

    如需详细了解如何通过 Flutter 使用 reCAPTCHA,请参阅 Flutter 文档

    ReactNative

    如需详细了解如何通过 React Native 使用 reCAPTCHA,请参阅 React Native 文档

    直接下载

    1. 如果您想以 xcframework 的形式下载 SDK 及其依赖项,请下载客户端

将 reCAPTCHA 与 iOS 应用集成

如需将 reCAPTCHA 与您的 iOS 应用集成,请在 Xcode 中执行以下步骤:

  1. 如需使用您创建的 reCAPTCHA 密钥 (KEY_ID) 实例化 SDK,请使用以下代码更新应用:

    Swift(使用情节提要)

    1. 更新 ViewController.swift

      import RecaptchaEnterprise
      
      class ViewController: UIViewController {
        var recaptchaClient: RecaptchaClient?
      
        override func viewDidLoad() {
          super.viewDidLoad()
          Task {
            do {
              self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID")
            } catch let error as RecaptchaError {
               print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
            }
          }
        }
      }
      

      如果应用的最低操作系统版本低于 13,请改用尾随闭包:

      import RecaptchaEnterprise
      
      class ViewController: UIViewController {
        var recaptchaClient: RecaptchaClient?
      
        override func viewDidLoad() {
          super.viewDidLoad()
          Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in
            guard let client = client else {
                print("RecaptchaClient creation error: \(error).")
              return
            }
            self.recaptchaClient = client
          }
        }
      }
      

    Swift 与 SwiftUI

    1. 创建 ViewModel 类。

      import RecaptchaEnterprise
      
      @MainActor class ViewModel: ObservableObject {
        private var recaptchaClient: RecaptchaClient?
      
        init() {
           Task {
            do {
              self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID")
            } catch let error as RecaptchaError {
               print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
            }
          }
        }
      }
      

      如果应用的最低操作系统版本低于 13,请改用尾随闭包:

      import RecaptchaEnterprise
      
      class ViewController: UIViewController {
        var recaptchaClient: RecaptchaClient?
      
        override func viewDidLoad() {
          super.viewDidLoad()
          Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in
            guard let client = client else {
                print("RecaptchaClient creation error: \(error).")
              return
            }
            self.recaptchaClient = client
          }
        }
      }
      
    2. ContentView.swift 中实例化 ViewModel

      import SwiftUI
      import RecaptchaEnterprise
      
      struct ContentView: View {
        @StateObject private var viewModel = ViewModel()
      
        var body: some View {
        }
      }
      
      struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
          ContentView()
        }
      }
      

    Objective-C

    1. 如果您的应用是用 Objective-C 编写的,请创建一个虚拟 Swift 文件,并包含以下导入,以确保 Xcode 可以找到并链接 Swift 库。

      import Foundation
      
    2. 为确保 Swift 代码已正确关联,请依次前往目标 > 构建设置 > 始终嵌入 Swift 标准库,然后验证该选项是否已设置为 Yes

    3. 更新 ViewController.h

      #import <RecaptchaEnterprise/RecaptchaEnterprise.h>
      
      @interface ViewController : UIViewController
      @property (strong, atomic) RecaptchaClient *recaptchaClient;
      @end
      
    4. 更新 ViewController.m

      @implementation ViewController
      [Recaptcha fetchClientWithSiteKey:@"KEY_ID"
            completion:^void(RecaptchaClient* recaptchaClient, NSError* error) {
              if (!recaptchaClient) {
                NSLog(@"%@", (RecaptchaError *)error.errorMessage);
                return;
              }
              self->_recaptchaClient = recaptchaClient;
            }
      ];
      @end
      

    SDK 的初始化可能需要几秒钟才能完成。为了缓解此延迟,请尽早初始化客户端,例如在自定义 Application 类的 onCreate() 调用期间。您不应让界面元素阻塞 reCAPTCHA SDK。

  2. 创建一个按钮来调用 reCAPTCHA 并触发 execute()

    Swift(使用情节提要)

    1. 在分镜脚本中,创建一个按钮。
    2. ViewController 中创建与您创建的按钮关联的操作。
    3. 使用以下代码段调用 execute() 方法并传递 Login 操作,以返回 reCAPTCHA 令牌:

      guard let recaptchaClient = recaptchaClient else {
        print("RecaptchaClient creation failed.")
        return
      }
      Task {
        do {
          let token = try await recaptchaClient.execute(withAction: RecaptchaAction.login)
          print(token)
        } catch let error as RecaptchaError {
          print(error.errorMessage)
        }
      }
      

      如果应用的最低操作系统版本低于 13,请改用尾随闭包:

      guard let recaptchaClient = recaptchaClient else {
        print("RecaptchaClient creation failed.")
        return
      }
      recaptchaClient.execute(withAction: RecaptchaAction.login) { token, error in
        if let token = token {
          print(token)
        } else {
          print(error)
        }
      }
      

    Swift 与 SwiftUI

    1. 使用执行代码更新 ViewModel.swift:

      import RecaptchaEnterprise
      
      @MainActor class ViewModel: ObservableObject {
      
        func execute() {
          guard let recaptchaClient = self.recaptchaClient else {
            print("Client not initialized correctly.")
            return
          }
      
          Task {
            do {
              let token = try await recaptchaClient.execute(withAction: RecaptchaAction.login)
              print(token)
            } catch let error as RecaptchaError {
              print(error.errorMessage)
            }
          }
        }
      }
      

      如果应用的最低操作系统版本低于 13,请改用尾随闭包:

      guard let recaptchaClient = recaptchaClient else {
        print("RecaptchaClient creation failed.")
        return
      }
      recaptchaClient.execute(withAction: RecaptchaAction.login) { token, error in
        if let token = token {
          print(token)
        } else {
          print(error)
        }
      }
      
    2. 更新 ContentView.swift。

      import SwiftUI
      import RecaptchaEnterprise
      
      struct ContentView: View {
        @StateObject private var viewModel = ViewModel()
      
        var body: some View {
      
          Button {
            viewModel.execute()
          } label: {
            Text("Execute")
          }.padding()
      
          Spacer()
        }
      }
      
      struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
          ContentView()
        }
      }
      

    Objective-C

    1. 分镜脚本中,创建一个按钮。
    2. ViewController 中创建与您创建的按钮关联的操作。
    3. 调用 execute() 方法并传递 Login 操作,以返回 reCAPTCHA 令牌:

      if (!self->_recaptchaClient) {
        return;
      }
      
      [recaptchaClient execute:RecaptchaAction.login
          completion:^void(NSString* _Nullable  token, NSError* _Nullable error) {
        if (!token) {
          NSLog (@"%@", (RecaptchaError *)error.errorMessage);
          return;
        }
        NSLog (@"%@", token);
      }];
      

    客户端的 execute API 可能需要几秒钟才能完成,例如在网络条件较差的情况下,或者在等待后台初始化完成时。确保 execute() 调用不会阻塞界面事件,例如按按钮。

  3. 测试您的应用:

    1. reCAPTCHA 使用 Apple 的 App Attest 作为其检测引擎的一部分。如果您不打算使用具有固定得分的测试密钥进行本地开发,请执行以下操作:

      1. 在 Xcode 中,将 App Attest 功能添加到您的应用中。

      2. 在项目的 .entitlements 文件中,将 App Attest 环境设置为 production

    2. 如需清理 Xcode 构建环境,请在产品菜单中点击清理构建文件夹

    3. 如要运行该应用,请在 Product 菜单中点击运行

    4. 在已加载的应用中,点击您之前创建的按钮。

    5. 观察调试输出窗口,如果集成正确完成,将返回一个 reCAPTCHA 令牌(字母数字字符串)。

从方法 API 迁移到 fetchClient 方法

fetchClient 方法会返回一个 RecaptchaClient,该客户端会在网络故障时重试初始化。如果应用在创建客户端时没有网络访问权限,客户端会不断重试,并在获得网络后成功初始化。

如果您调用 execute(timeout),但客户端尚未准备就绪,则它会尝试先进行初始化,然后再返回令牌或 RecaptchaErrorCode

以下示例展示了如何从 getClient 迁移到 fetchClient

Swift(使用情节提要)

// Migrate from getClient
func initializeWithGetClient() {
  Task {
    do {
      self.recaptchaClient = try await Recaptcha.getClient(withSiteKey: "KEY_ID")
    } catch let error as RecaptchaError {
        print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
    }
  }
}

// Migrate to fetchClient
func initializeWithFetchClient() {
  Task {
    do {
      self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID")
    } catch let error as RecaptchaError {
        print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
    }
  }
}

如果应用的最低操作系统版本低于 13,请改用尾随闭包:

// Migrate from getClient
override func initializeWithGetClient() {
  Recaptcha.getClient(withSiteKey: "KEY_ID") { client, error in
    guard let client = client else {
        print("RecaptchaClient creation error: \(error).")
      return
    }
    self.recaptchaClient = client
  }
}

// Migrate to fetchClient
override func initializeWithFetchClient() {
  Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in
    guard let client = client else {
        print("RecaptchaClient creation error: \(error).")
      return
    }
    self.recaptchaClient = client
  }
}

Swift 与 SwiftUI

// Migrate from getClient
initializeWithGetClient() {
    Task {
    do {
      self.recaptchaClient = try await Recaptcha.getClient(withSiteKey: "KEY_ID")
    } catch let error as RecaptchaError {
        print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
    }
  }
}

// Migrate to fetchClient
initializeWithFetchClient() {
    Task {
    do {
      self.recaptchaClient = try await Recaptcha.fetchClient(withSiteKey: "KEY_ID")
    } catch let error as RecaptchaError {
        print("RecaptchaClient creation error: \(String(describing: error.errorMessage)).")
    }
  }
}

如果应用的最低操作系统版本低于 13,请改用尾随闭包:

// Migrate from getClient
func initializeWithGetClient() {
  super.viewDidLoad()
  Recaptcha.getClient(withSiteKey: "KEY_ID") { client, error in
    guard let client = client else {
        print("RecaptchaClient creation error: \(error).")
      return
    }
    self.recaptchaClient = client
  }
}

// Migrate to fetchClient
func initializeWithFetchClient() {
  super.viewDidLoad()
  Recaptcha.fetchClient(withSiteKey: "KEY_ID") { client, error in
    guard let client = client else {
        print("RecaptchaClient creation error: \(error).")
      return
    }
    self.recaptchaClient = client
  }
}

Objective-C

// Migrate from getClient
@implementation ViewController
[Recaptcha getClientWithSiteKey:@"KEY_ID"
      completion:^void(RecaptchaClient* recaptchaClient, NSError* error) {
        if (!recaptchaClient) {
          NSLog(@"%@", (RecaptchaError *)error.errorMessage);
          return;
        }
        self->_recaptchaClient = recaptchaClient;
      }
];
@end

// Migrate to fetchClient
@implementation ViewController
[Recaptcha fetchClientWithSiteKey:@"KEY_ID"
      completion:^void(RecaptchaClient* recaptchaClient, NSError* error) {
        if (!recaptchaClient) {
          NSLog(@"%@", (RecaptchaError *)error.errorMessage);
          return;
        }
        self->_recaptchaClient = recaptchaClient;
      }
];
@end

为 API 调用设置超时时间

您可以使用 withTimeout 属性为 execute API 指定超时值。

Swift

  1. 调用 execute 时设置超时。

      Task {
        do {
          let token = try await recaptchaClient.execute(
            withAction: RecaptchaAction.login,
            withTimeout: 10000)
          print(token)
        } catch let error as RecaptchaError {
          print(error.errorMessage)
        }
      }
    

    如果应用的最低操作系统版本低于 13,请改用尾随闭包:

      recaptchaClient.execute(
        withAction: RecaptchaAction.login,
        withTimeout: 10000
      ) { token, error in
        if let token = token {
          print(token)
        } else {
          print(error)
        }
      }
    

Objective-C

  1. 调用 execute 时设置超时。

      [recaptchaClient execute:RecaptchaAction.login
          witTimeout:10000.0
          completion:^void(NSString* _Nullable  token, NSError* _Nullable error) {
        if (!token) {
          NSLog (@"%@", (RecaptchaError *)error.errorMessage);
          return;
        }
        NSLog (@"%@", token);
      }];
    

处理错误

如果您的应用无法成功与 reCAPTCHA 服务通信,则可能是因为 API 遇到了错误。您必须在应用中添加逻辑,以妥善处理此类错误。

如需详细了解常见 API 错误的缓解措施,请参阅 RecaptchaErrorCode

API 参考文档

如需 iOS 版 reCAPTCHA API 的完整参考信息,请参阅 RecaptchaEnterprise

后续步骤

  • 要评估 reCAPTCHA 响应令牌,请创建评估