实现“使用 Google 账号登录”功能

本指南介绍了如何实现“使用 Google 账号登录”,并涵盖了以下步骤:

  • 为应用添加依赖项。
  • 实例化 CredentialManager
  • 创建底部动作条流程。
  • 创建按钮流。
  • 处理登录响应。
  • 处理错误。
  • 处理退出账号操作。

为应用添加依赖项

在模块的 build.gradle 文件中,使用最新版的 Credential Manager、Play 服务身份验证googleid 声明依赖项:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.6.0-rc02")
    implementation("androidx.credentials:credentials-play-services-auth:1.6.0-rc02")
    implementation("com.google.android.libraries.identity.googleid:googleid:<latest version>")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.6.0-rc02"
    implementation "androidx.credentials:credentials-play-services-auth:1.6.0-rc02"
    implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}

实例化 Credential Manager

使用应用或 activity 上下文创建 CredentialManager 对象。

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

创建底部动作条流程

底部动作条是 Credential Manager 的内置界面。使用此界面可在所有身份验证方法(例如密码、通行密钥和“使用 Google 账号登录”)中提供一致的体验。

为之前已获授权的账号配置登录请求

尝试使用 GetGoogleIdOption 发出 Google 登录请求,以检索用户的 Google ID 令牌。

以下代码段用于检查账号是否为已获授权的账号。

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
    .setFilterByAuthorizedAccounts(true)
    .setServerClientId(WEB_CLIENT_ID)
    .setAutoSelectEnabled(true)
    .setNonce(generateSecureRandomNonce())
    .build()

请求 googleIdOption 对象的配置如下:

  • 过滤之前已获授权的账号:如需检索之前曾用于登录您应用的已获授权的账号,请将 setFilterByAuthorizedAccounts 设置为 true

    请注意,setFilterByAuthorizedAccounts 的默认值为 true,这意味着底部动作条界面的默认行为是仅显示之前已获授权的账号。

  • 设置服务器客户端 ID:设置 setServerClientId 参数。webClientId 是您在完成前提条件时在 Google Cloud 项目中为 OAuth 设置的 Web 客户端 ID。

  • 启用自动登录功能(可选):如需为回访用户启用自动登录功能,请使用 setAutoSelectEnabled(true)setFilterByAuthorizedAccounts(true)。对于应用用户,如果他们之前已登录,则可避免不必要的麻烦。

    只有在满足以下条件时,用户才能自动登录:

    • 设备上只有一个已获授权的账号,并且该已获授权的账号之前曾用于登录设备上的应用。设备上的多个授权账号会停用自动登录功能。
    • 用户在之前的会话期间未明确退出应用。
    • 用户尚未在其 Google 账号设置中停用自动登录功能。
  • 设置 Nonce(可选):如需增强安全性,请为服务器端验证设置 Nonce。为防止重放攻击,您可以添加一个随机数,以便使用 setNonce() 进行服务器端验证。确保您的服务器端代码验证请求和响应 nonce 是否相同。

    如需生成 Nonce,请使用类似于以下 Functions 的函数,该函数会生成指定长度的加密型强随机 Nonce,并使用 Base64 对其进行编码:

fun generateSecureRandomNonce(byteLength: Int = 32): String {
    val randomBytes = ByteArray(byteLength)
    SecureRandom().nextBytes(randomBytes)
    return Base64.encodeToString(randomBytes, Base64.NO_WRAP or Base64.URL_SAFE or Base64.NO_PADDING)
}

请求登录

通过调用 getCredential 方法检查用户在设备上是否拥有已获授权的账号:

val request: GetCredentialRequest = GetCredentialRequest.Builder()
    .addCredentialOption(googleIdOption)
    .build()

coroutineScope {
    try {
        val result = credentialManager.getCredential(
            request = request,
            context = activityContext,
        )
        handleSignIn(result)
    } catch (e: GetCredentialException) {
        // Handle failures
    }
}

配置在没有授权账号的情况下发出的登录请求

如果设备上没有应用的授权用户,则 CredentialManager 会返回 NoCredentialException。在这种情况下,请停用已获授权的账号过滤器,以便用户可以使用其他账号进行注册。

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
    .setFilterByAuthorizedAccounts(false)
    .setServerClientId(WEB_CLIENT_ID)
    .setNonce(generateSecureRandomNonce())
    .build()

接下来,请求登录,这与您为授权账号执行的操作类似。

创建按钮流程

如果您希望用户能够在以下情况下使用“使用 Google 账号登录”功能,请使用按钮:

  • 用户关闭了 Credential Manager 底部动作条界面。
  • 此设备上没有 Google 账号。
  • 设备上的现有账号需要重新验证身份。

创建按钮界面

虽然可以使用 Jetpack Compose 按钮完成此操作,但您可以使用“使用 Google 账号登录”品牌推广指南页面中预先批准的品牌图标。

创建登录流程

使用 GetSignInWithGoogleOption 创建 Google 登录请求,以检索 Google ID 令牌。

val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder(
    serverClientId = WEB_CLIENT_ID
).setNonce(generateSecureRandomNonce())
    .build()

接下来,请求登录,这与您对底部动作条界面执行的操作类似。

为底部动作条和按钮创建共享的登录功能

如需处理登录,请完成以下步骤:

  1. 使用 CredentialManager 的 getCredential() 函数。如果响应成功,请提取 CustomCredential,其类型应为 GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
  2. 使用 GoogleIdTokenCredential.createFrom() 方法将对象转换为 GoogleIdTokenCredential

  3. 在信赖方服务器上验证凭据。

  4. 确保您已正确处理错误。

fun handleSign(result: GetCredentialResponse) {
    // Handle the successfully returned credential.
    val credential = result.credential

    when (credential) {
        is CustomCredential -> {
            if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
                try {
                    // Use googleIdTokenCredential and extract the ID for server-side validation.
                    val googleIdTokenCredential = GoogleIdTokenCredential
                        .createFrom(credential.data)
                } catch (e: GoogleIdTokenParsingException) {
                    Log.e(TAG, "Received an invalid google id token response", e)
                }
            } else {
                // Catch any unrecognized credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }

        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential")
        }
    }
}

处理错误

查看问题排查中列出的错误,确保您的代码能够处理所有可能的错误情形。

处理退出账号操作

请务必为用户提供一种从应用中退出账号的机制。 例如,用户可能在设备上拥有多个 Google 账号,并决定从其他账号登录。您可以在设置页面等位置提供此信息。

凭据提供程序可能会存储有效的凭据会话,并使用该会话来限制未来登录请求的登录选项。例如,它可以优先使用有效凭据,而不是任何其他可用凭据。

当用户从应用中退出账号时,请调用 API clearCredentialState() 方法以清除所有凭据提供程序的当前用户凭据状态。这会通知所有凭据提供方,应清除给定应用的所有已存储的凭据会话,以便在用户下次登录时提供完整的登录选项。