关联账号登录可让已将其 Google 账号关联到您的服务的用户使用通过 Google 一次轻触登录功能。这改善了用户体验,因为他们可以通过一次点击登录,而无需重新输入用户名和密码。它还可以减少用户在您的服务上创建重复账号的可能性。
关联账号登录是 Android 版 One Tap 登录流程的一部分。这意味着如果您的应用已启用 One Tap 功能,则无需导入单独的库。
本文档将介绍如何修改您的 Android 应用以支持关联账号登录。
工作原理
- 您选择在 One Tap 登录流程中显示关联账号。
- 如果用户已登录 Google 并将其 Google 账号与他们在您服务上的账号关联,则会返回关联账号的 ID 令牌。
- 系统会向用户显示 One Tap 登录提示,其中包含使用其关联账号登录您的服务的选项。
- 如果用户选择继续使用关联账号,则用户的 ID 令牌将返回到您的应用。您将此令牌与步骤 2 中发送到您的服务器的令牌进行匹配,以识别已登录的用户。
设置
设置您的开发环境
在您的开发主机上获取最新的 Google Play 服务
在 SDK Tools 下,找到 Google Play services。
如果这些软件包的状态不是“已安装”,请同时选中它们,然后点击 Install Packages。
配置您的应用
在您的项目级
build.gradle文件中,在buildscript和allprojects部分都包含 Google 的 Maven 代码库。buildscript { repositories { google() } } allprojects { repositories { google() } }将“Link with Google”API 的依赖项添加到您的模块应用级 gradle 文件中,该文件通常为
app/build.gradledependencies { implementation 'com.google.android.gms:play-services-auth:21.3.0' }
修改您的 Android 应用以支持关联账号登录
在关联账号登录流程结束时,ID 令牌会返回到您的应用。在用户登录之前,应验证 ID 令牌的完整性。
以下代码示例详细介绍了检索、验证 ID 令牌以及后续让用户登录的步骤。
创建一个 Activity 来接收登录 Intent 的结果
Kotlin
private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.StartIntentSenderForResult()) { result -> if (result.resultCode == RESULT_OK) { try { val signInCredentials = Identity.signInClient(this) .signInCredentialFromIntent(result.data) // Review the Verify the integrity of the ID token section for // details on how to verify the ID token verifyIdToken(signInCredential.googleIdToken) } catch (e: ApiException) { Log.e(TAG, "Sign-in failed with error code:", e) } } else { Log.e(TAG, "Sign-in failed") } }Java
private final ActivityResultLauncher<IntentSenderResult> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), result -> { If (result.getResultCode() == RESULT_OK) { try { SignInCredential signInCredential = Identity.getSignInClient(this) .getSignInCredentialFromIntent(result.getData()); verifyIdToken(signInCredential.getGoogleIdToken()); } catch (e: ApiException ) { Log.e(TAG, "Sign-in failed with error:", e) } } else { Log.e(TAG, "Sign-in failed") } });构建登录请求
Kotlin
private val tokenRequestOptions = GoogleIdTokenRequestOptions.Builder() .supported(true) // Your server's client ID, not your Android client ID. .serverClientId(getString("your-server-client-id") .filterByAuthorizedAccounts(true) .associateLinkedAccounts("service-id-of-and-defined-by-developer", scopes) .build()Java
private final GoogleIdTokenRequestOptions tokenRequestOptions = GoogleIdTokenRequestOptions.Builder() .setSupported(true) .setServerClientId("your-service-client-id") .setFilterByAuthorizedAccounts(true) .associateLinkedAccounts("service-id-of-and-defined-by-developer", scopes) .build()启动登录 Pending Intent
Kotlin
Identity.signInClient(this) .beginSignIn( BeginSignInRequest.Builder() .googleIdTokenRequestOptions(tokenRequestOptions) .build()) .addOnSuccessListener{result -> activityResultLauncher.launch(result.pendingIntent.intentSender) } .addOnFailureListener {e -> Log.e(TAG, "Sign-in failed because:", e) }Java
Identity.getSignInClient(this) .beginSignIn( BeginSignInRequest.Builder() .setGoogleIdTokenRequestOptions(tokenRequestOptions) .build()) .addOnSuccessListener(result -> { activityResultLauncher.launch( result.getPendingIntent().getIntentSender()); }) .addOnFailureListener(e -> { Log.e(TAG, "Sign-in failed because:", e); });
验证 ID 令牌的完整性
使用 Google API 客户端库
使用Java Google API 客户端库是在生产环境中验证 Google ID 令牌的推荐方法。
Java
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
...
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
// Specify the CLIENT_ID of the app that accesses the backend:
.setAudience(Collections.singletonList(CLIENT_ID))
// Or, if multiple clients access the backend:
//.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
.build();
// (Receive idTokenString by HTTPS POST)
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
// Print user identifier
String userId = payload.getSubject();
System.out.println("User ID: " + userId);
// Get profile information from payload
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
// Use or store profile information
// ...
} else {
System.out.println("Invalid ID token.");
}
GoogleIdTokenVerifier.verify() 方法验证 JWT 签名、aud 声明、iss 声明以及 exp 声明。
如果您需要验证 ID 令牌是否代表 Google Workspace 或 Cloud 组织账号,您可以通过检查 Payload.getHostedDomain() 方法返回的域名来验证 hd 声明。