使用 Google 账号登录可帮助您快速将用户身份验证与 Android 应用集成。用户可以使用其 Google 账号登录您的应用、提供同意,并安全地与您的应用共享其个人资料信息。Android 的 Credential Manager Jetpack 库可让此集成顺利进行,并使用单个 API 在各种 Android 设备上提供一致的体验。
本文档将引导您在 Android 应用中实现“使用 Google 账号登录”功能,介绍如何设置“使用 Google 账号登录”按钮界面,以及如何配置经过优化的应用一键式注册和登录体验。为了顺利完成设备迁移,“使用 Google 账号登录”功能支持自动登录,并且它在 Android、iOS 和 Web 平台上的跨平台特性有助于您在任何设备上为应用提供登录权限。
如需设置“使用 Google 账号登录”,请按以下两个主要步骤操作:
将“使用 Google 账号登录”配置为 Credential Manager 底部动作条界面的选项。您可以将其配置为自动提示用户登录。 如果您已实现通行密钥或密码,则可以同时请求所有相关凭据类型,以便用户无需记住之前登录时所用的选项。
将“使用 Google 账号登录”按钮添加到应用的界面中。借助“使用 Google 账号登录”按钮,用户可以使用其现有的 Google 账号以简化方式注册或登录 Android 应用。如果用户关闭底部动作条界面,或者明确希望使用 Google 账号进行注册和登录,则会点击“使用 Google 账号登录”按钮。对于开发者而言,这意味着用户更容易上手,注册流程更顺畅。
本文将介绍如何使用 Google ID 辅助库将“使用 Google 账号登录”按钮和底部动作条对话框与 Credential Manager API 集成。
设置 Google API 控制台项目
- 在 API 控制台中打开您的项目,或者创建一个项目(如果您还没有项目)。
- 在 OAuth 同意屏幕页面上,确保所有信息完整且准确。
- 请确保为您的应用分配了正确的应用名称、应用徽标和应用首页。在用户注册时,系统会在“使用 Google 账号登录”意见征求界面和“第三方应用和服务”界面上向用户显示这些值。
- 请确保您已指定应用的隐私权政策和服务条款的网址。
- 在“凭据”页面中,为您的应用创建 Android 客户端 ID(如果您还没有)。您需要指定应用的软件包名称和 SHA-1 签名。
- 前往“凭据”页面。
- 依次点击创建凭据 > OAuth 客户端 ID。
- 选择 Android 应用类型。
- 在“凭据”页面中,创建新的“Web 应用”客户端 ID(如果尚未创建)。您暂时可以忽略“已获授权的 JavaScript 来源”和“已获授权的重定向 URI”字段。当后端服务器与 Google 的身份验证服务进行通信时,此客户端 ID 将用于标识该服务器。
- 前往“凭据”页面。
- 依次点击创建凭据 > OAuth 客户端 ID。
- 选择 Web 应用类型。
声明依赖项
在模块的 build.gradle 文件中,使用最新版 Credential Manager 声明依赖项:
dependencies {
// ... other dependencies
implementation "androidx.credentials:credentials:<latest version>"
implementation "androidx.credentials:credentials-play-services-auth:<latest version>"
implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}
实例化 Google 登录请求
如需开始构建您的实现,请实例化 Google 登录请求。使用 GetGoogleIdOption
检索用户的 Google ID 令牌。
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(true)
.setServerClientId(WEB_CLIENT_ID)
.setAutoSelectEnabled(true)
.setNonce(<nonce string to use when generating a Google ID token>)
.build()
首先,通过将 setFilterByAuthorizedAccounts
参数设置为 true
调用 API,检查用户是否有任何之前用于登录应用的账号。用户可以从可用账号中选择一个账号进行登录。
如果没有可用的已获授权的 Google 账号,系统应提示用户使用其任何可用的账号进行注册。为此,请再次调用 API 并将 setFilterByAuthorizedAccounts
设置为 false
,以提示用户。详细了解如何注册。
为回访用户启用自动登录功能(推荐)
开发者应为使用单个账号注册的用户启用自动登录功能。这样一来,用户便可以在不同设备上获得无缝体验,尤其是在设备迁移期间,用户无需重新输入凭据,即可快速恢复对账号的访问权限。对于用户而言,这可避免他们在之前已登录的情况下遇到不必要的麻烦。
如需启用自动登录功能,请使用 setAutoSelectEnabled(true)
。只有在满足以下条件时,用户才能自动登录:
- 只有一个凭据与请求匹配,该凭据可以是 Google 账号或密码,并且该凭据与 Android 设备上的默认账号匹配。
- 用户尚未明确退出账号。
- 用户尚未在其 Google 账号设置中停用自动登录功能。
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(true)
.setServerClientId(WEB_CLIENT_ID)
.setAutoSelectEnabled(true)
.setNonce(<nonce string to use when generating a Google ID token>)
.build()
实现自动登录时,请务必正确处理退出,以便用户在明确退出您的应用后始终可以选择正确的账号。
设置 Nonce 以提高安全性
为了提高登录安全性并避免重放攻击,请添加 setNonce
以在每个请求中添加 Nonce。详细了解如何生成 Nonce。
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(true)
.setServerClientId(WEB_CLIENT_ID)
.setAutoSelectEnabled(true)
.setNonce(<nonce string to use when generating a Google ID token>)
.build()
创建“使用 Google 账号登录”流程
设置“使用 Google 账号登录”流程的步骤如下:
- 实例化
GetCredentialRequest
,然后使用addCredentialOption()
添加之前创建的googleIdOption
以检索凭据。 - 将此请求传递给
getCredential()
(Kotlin) 或getCredentialAsync()
(Java) 调用以检索用户的可用凭据。 - API 成功运行后,提取存放
GoogleIdTokenCredential
数据结果的CustomCredential
。 CustomCredential
的类型应等于GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
的值。使用GoogleIdTokenCredential.createFrom
方法将该对象转换为GoogleIdTokenCredential
。如果转换成功,请提取
GoogleIdTokenCredential
ID,对其进行验证,并在服务器上对凭据进行身份验证。如果转换失败并显示
GoogleIdTokenParsingException
,那么您可能需要更新“使用 Google 账号登录”库的版本。捕获任何无法识别的自定义凭据类型。
val request: GetCredentialRequest = Builder()
.addCredentialOption(googleIdOption)
.build()
coroutineScope.launch {
try {
val result = credentialManager.getCredential(
request = request,
context = activityContext,
)
handleSignIn(result)
} catch (e: GetCredentialException) {
handleFailure(e)
}
}
fun handleSignIn(result: GetCredentialResponse) {
// Handle the successfully returned credential.
val credential = result.credential
when (credential) {
// Passkey credential
is PublicKeyCredential -> {
// Share responseJson such as a GetCredentialResponse on your server to
// validate and authenticate
responseJson = credential.authenticationResponseJson
}
// Password credential
is PasswordCredential -> {
// Send ID and password to your server to validate and authenticate.
val username = credential.id
val password = credential.password
}
// GoogleIdToken credential
is CustomCredential -> {
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
try {
// Use googleIdTokenCredential and extract the ID to validate and
// authenticate on your server.
val googleIdTokenCredential = GoogleIdTokenCredential
.createFrom(credential.data)
// You can use the members of googleIdTokenCredential directly for UX
// purposes, but don't use them to store or control access to user
// data. For that you first need to validate the token:
// pass googleIdTokenCredential.getIdToken() to the backend server.
GoogleIdTokenVerifier verifier = ... // see validation instructions
GoogleIdToken idToken = verifier.verify(idTokenString);
// To get a stable account identifier (e.g. for storing user data),
// use the subject ID:
idToken.getPayload().getSubject()
} catch (e: GoogleIdTokenParsingException) {
Log.e(TAG, "Received an invalid google id token response", e)
}
} else {
// Catch any unrecognized custom 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 账号登录”按钮流程
如需触发“使用 Google 账号登录”按钮流程,请使用 GetSignInWithGoogleOption
而非 GetGoogleIdOption
:
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder()
.setServerClientId(WEB_CLIENT_ID)
.setNonce(<nonce string to use when generating a Google ID token>)
.build()
处理返回的 GoogleIdTokenCredential
,如以下代码示例所述。
fun handleSignIn(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 id to validate and
// authenticate on your server.
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 账号登录请求后,按照使用 Google 账号登录部分中所述的相似方式启动身份验证流程。
为新用户启用注册功能(推荐)
“使用 Google 账号登录”是用户创建新账号的最简单方式,只需点按几下,即可在您的应用或服务中创建新账号。
如果未找到任何已保存的凭据(getGoogleIdOption
未返回任何 Google 账号),请提示用户注册。首先,检查 setFilterByAuthorizedAccounts(true)
,看看是否存在之前使用过的账号。如果未找到任何用户,请使用 setFilterByAuthorizedAccounts(false)
提示用户使用 Google 账号进行注册
示例:
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(WEB_CLIENT_ID)
.build()
实例化 Google 账号注册请求后,启动身份验证流程。如果用户不想使用“使用 Google 账号登录”功能进行注册,不妨考虑优化应用以支持自动填充功能。用户创建账号后,不妨考虑在账号创建的最后一步为其注册通行密钥。
处理退出账号操作
当用户从应用中退出账号时,请调用 API clearCredentialState()
方法以清除所有凭据提供程序中的当前用户凭据状态。这会通知所有凭据提供程序,应清除给定应用的所有存储凭据会话。
凭据提供程序可能会存储有效的凭据会话,并使用该会话来限制日后 get-credential 调用的登录选项。例如,它可能会优先使用有效凭据,而不是任何其他可用凭据。当用户明确退出您的应用时,为了在下次获取完整的登录选项,您应调用此 API 以让提供程序清除所有存储的凭据会话。