将 reCAPTCHA 与 iOS 应用集成

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

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

该 SDK 利用反射和动态代码,以便更新和优化已部署的应用或 SDK 中的检测系统。为避免干扰应用,系统中可用的一组类仅限于精心控制的列表。

准备工作

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

  2. 为 reCAPTCHA 准备环境

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

    或者,通过执行以下步骤之一,复制现有 iOS 版 reCAPTCHA 密钥的 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.6.0"
        ...
      
      end
      
    3. 运行 pod update 以安装所需的依赖项。

    Swift Package Manager

    1. 在 Xcode 中,依次选择 File > Add Packages,然后在 SearchEnter Package 网址 字段中输入以下网址: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. 如果您想以 xcframeworks 的形式下载 SDK 及其依赖项,请下载客户端

配置应用

您可以使用 Swift 或 Objective-C 编写应用。

如需配置您的应用,请在应用中添加以下文件:

Swift

  1. 如果您的应用是用 Swift 编写的,请添加以下导入:

     import RecaptchaEnterprise
    

Objective-C

  1. 如果您的应用是用 Objective-C 编写的,请创建一个虚构的 Swift 文件,并添加以下导入,以确保 Xcode 能够找到并关联 Swift 库。

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

将 reCAPTCHA 与 iOS 应用集成

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

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

    Swift 与 Storyboard

    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. 更新了 ViewController.h

      #import <RecaptchaEnterprise/RecaptchaEnterprise.h>
      
      @interface ViewController : UIViewController
      @property (strong, atomic) RecaptchaClient *recaptchaClient;
      @end
      
    2. 更新了 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 与 Storyboard

    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 的 AppAttest。如果您不打算使用具有固定分数的测试密钥进行本地开发,请执行以下操作:

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

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

    2. 如需清理 Xcode 构建环境,请在 Product 菜单中点击 Clean Build Folder

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

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

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

从方法 API 迁移到 fetchClient 方法

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

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

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

Swift 与 Storyboard

// 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 响应令牌,请创建评估