“关联的账号登录”功能可让用户一键登录 Google 已关联 Google 账号和您的服务的用户。这可以改善 因为他们只需点击一下即可登录,无需再次进入, 用户名和密码进行登录。还能降低 您的服务上存在重复的账号。
“关联的账号登录”是以下账号的“一键登录”流程的一部分: Android。这意味着,如果您的应用 已启用一键式功能。
在本文档中,您将了解如何修改 Android 应用以支持 关联的账号登录。
工作原理
- 您可以在“一键登录”流程中选择显示关联的账号。
- 如果用户已登录 Google 账号,并已将自己的 Google 账号与 就会返回一个 ID 令牌,用于关联 。
- 系统会向用户显示一键登录提示,并提供登录您 关联的账号。
- 如果用户选择继续使用关联的账号,则用户的 ID 令牌 返回给您的应用您需要将此令牌与发送到 服务器识别已登录的用户。
设置
设置您的开发环境
在开发主机上获取最新的 Google Play 服务:
- 打开 Android SDK 管理器。
在 SDK Tools 下,找到 Google Play 服务。
如果这些软件包的状态未为“已安装”,请同时选中它们并点击 安装软件包。
配置您的应用
在项目级
build.gradle
文件中,添加 Google 的 Maven 制品库 在buildscript
和allprojects
部分中均有提供。buildscript { repositories { google() } } allprojects { repositories { google() } }
为“与 Google 关联”添加依赖项API 添加到您的模块中, 应用级 Gradle 文件,通常为
app/build.gradle
:dependencies { implementation 'com.google.android.gms:play-services-auth:21.2.0' }
修改您的 Android 应用以支持关联的账号登录
在“关联的账号登录”流程结束后,系统会将一个 ID 令牌返回给您的 应用。在用户登录之前,应验证 ID 令牌的完整性。
以下代码示例详细介绍了检索 验证 ID 令牌 使用户登录
创建一个 activity 以接收 Sign-In 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()
启动登录待处理 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 令牌的完整性
如需验证令牌是否有效,请确保满足以下条件:
- ID 令牌由 Google 正确签名。使用 Google 的公钥(以 JWK 或 PEM 格式提供)来验证令牌的签名。这些密钥会定期轮替;请检查响应中的
Cache-Control
标头,以确定何时应再次检索它们。 - ID 令牌中
aud
的值等于应用的某个客户端 ID。通过进行这项检查,可以防止向恶意应用颁发的 ID 令牌用于访问应用后端服务器上同一用户的数据。 - ID 令牌中
iss
的值等于accounts.google.com
或https://accounts.google.com
。 - ID 令牌的过期时间 (
exp
) 尚未过去。 - 如果您需要验证该 ID 令牌代表的是 Google Workspace 或 Cloud 组织帐号,则可以检查
hd
声明,该声明指示用户的托管网域。仅允许特定网域的成员访问资源时,必须使用此方法。缺少此声明表示帐号不属于 Google 托管的网域。
我们强烈建议您使用适合您的平台的 Google API 客户端库或通用 JWT 库,而不是自行编写代码来执行这些验证步骤。对于开发和调试,您可以调用我们的 tokeninfo
验证端点。
使用 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
组织账号,您可以通过检查域名来验证 hd
所有权声明
由 Payload.getHostedDomain()
方法返回。
调用 tokeninfo 端点
若要验证用于调试的 ID 令牌签名,一种简单的方法是使用 tokeninfo
端点。调用此端点涉及到一个额外的网络请求,该网络请求会为您执行大部分验证,而您在自己的代码中测试适当的验证和载荷提取时。它不适合在生产代码中使用,因为请求可能会受到限制或出现间歇性错误。
如需使用 tokeninfo
端点验证 ID 令牌,请向该端点发出 HTTPS POST 或 GET 请求,并在 id_token
参数中传递您的 ID 令牌。例如,要验证令牌“XYZ123”,可发出以下 GET 请求:
https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
如果令牌已正确签名,并且 iss
和 exp
声明具有预期值,您会收到 HTTP 200 响应,其正文包含 JSON 格式的 ID 令牌声明。以下是示例响应:
{ // These six fields are included in all Google ID Tokens. "iss": "https://accounts.google.com", "sub": "110169484474386276334", "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com", "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com", "iat": "1433978353", "exp": "1433981953", // These seven fields are only included when the user has granted the "profile" and // "email" OAuth scopes to the application. "email": "testuser@gmail.com", "email_verified": "true", "name" : "Test User", "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg", "given_name": "Test", "family_name": "User", "locale": "en" }
如果您需要验证此 ID 令牌是否代表 Google Workspace 帐号,可以检查 hd
声明,该声明指示用户的托管网域。仅允许特定网域的成员访问资源时,必须使用此方法。缺少此声明表示帐号不属于 Google Workspace 托管网域。