使用 Google 账号登录可帮助您快速将用户身份验证与 Android 应用集成。用户可以使用其 Google 账号登录您的应用、提供同意,并安全地与您的应用共享其个人资料信息。Android 的 Credential Manager Jetpack 库可让此集成顺利进行,并使用单个 API 在各种 Android 设备上提供一致的体验。
本文档将引导您在 Android 应用中实现“使用 Google 账号登录”功能,介绍如何设置“使用 Google 账号登录”按钮界面,以及如何配置经过应用优化的一键式注册和登录体验。为了顺利完成设备迁移,“使用 Google 账号登录”功能支持自动登录,并且它在 Android、iOS 和 Web 平台上的跨平台特性有助于您在任何设备上为应用提供登录权限。
如需设置“使用 Google 账号登录”,请按以下两个主要步骤操作:
将“使用 Google 账号登录”配置为 Credential Manager 底部动作条界面的选项。您可以将其配置为自动提示用户登录。 如果您已实现通行密钥或密码,则可以同时请求所有相关凭据类型,这样用户就不必记住之前登录时所用的选项。
![Credential Manager 底部动作条](https://developer.android.google.cn/static/training/sign-in/images/credman-bottomsheet-animated.gif?hl=ca)
将“使用 Google 账号登录”按钮添加到应用的界面中。“使用 Google 账号登录”按钮可让用户以简化的方式使用现有 Google 账号注册或登录 Android 应用。如果用户关闭底部动作条界面,或者明确希望使用 Google 账号进行注册和登录,则会点击“使用 Google 账号登录”按钮。对于开发者来说,这意味着用户更容易上手,注册流程也更顺畅。
![动画演示:使用 Google 账号登录流程](https://developer.android.google.cn/static/training/sign-in/images/add-siwg-animated-2.gif?hl=ca)
本文将介绍如何使用 Google ID 辅助库将“使用 Google 账号登录”按钮和底部动作条对话框与 Credential Manager API 集成。
设置 项目
- 在 中打开您的项目,或者创建一个项目(如果您还没有项目)。
- 在 中,确保所有信息完整且准确。
- 请确保为您的应用分配了正确的应用名称、应用徽标和应用首页。在用户注册时,系统会在“使用 Google 账号登录”意见征求界面和“第三方应用和服务”界面上向用户显示这些值。
- 请确保您已指定应用的隐私权政策和服务条款的网址。
- 在 中,为您的应用创建 Android 客户端 ID(如果您尚无该 ID)。您需要指定应用的软件包名称和 SHA-1 签名。
- 在 中,创建一个新的“Web 应用”客户端 ID(如果您尚未创建)。您目前可以忽略“已获授权的 JavaScript 来源”和“已获授权的重定向 URI”字段。在后端服务器与 Google 的身份验证服务通信时,此客户端 ID 将用于标识您的后端服务器。
声明依赖项
在模块的 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 以让提供程序清除所有存储的凭据会话。