Google 授权登录 V2 接入文档
版本: V2.0
更新日期: 2026-05-27
适用平台: Android (API 23+)
技术方案: Credential Manager + Sign-In with Google
目录
一、概述
本文档介绍 Android 项目中使用 Credential Manager 接入 Google 授权登录(Sign-In with Google)的 V2 版本实现。该方案基于 androidx.credentials 库,替代了旧版的 Google Sign-In SDK。
技术演进
| 版本 | 方案 | 状态 |
|---|---|---|
| V1 (旧版) | com.google.android.gms:play-services-auth | 已废弃 |
| V2 (新版) | androidx.credentials + Credential Manager | 推荐使用 |
官方文档
- 新版文档:developer.android.com/identity/si…
- 旧版文档:developer.android.google.cn/identity/le…
- 参考博客:blog.csdn.net/zll18201518…
二、Gradle 依赖配置
在 app/build.gradle 中添加以下依赖:
dependencies {
// ═══════════════════════════════════════════
// Credential Manager 核心库
// ═══════════════════════════════════════════
implementation "androidx.credentials:credentials:1.2.2"
implementation "androidx.credentials:credentials-play-services-auth:1.2.2"
// ═══════════════════════════════════════════
// Google ID 令牌支持
// ═══════════════════════════════════════════
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
}
⚠️ 版本兼容警告
androidx.credentials1.7.0-alpha02+ 要求 Android Gradle Plugin 8.6.0+ 。
若项目使用 AGP 8.0.x,请使用 1.2.2 稳定版。
三、完整源码
TestGoogleSdk.java
package com.example.myapplication;
import android.app.Activity;
import android.os.CancellationSignal;
import android.text.TextUtils;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.credentials.ClearCredentialStateRequest;
import androidx.credentials.Credential;
import androidx.credentials.CredentialManager;
import androidx.credentials.CredentialManagerCallback;
import androidx.credentials.CustomCredential;
import androidx.credentials.GetCredentialRequest;
import androidx.credentials.GetCredentialResponse;
import androidx.credentials.exceptions.ClearCredentialException;
import androidx.credentials.exceptions.GetCredentialCancellationException;
import androidx.credentials.exceptions.GetCredentialException;
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption;
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential;
import org.json.JSONObject;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Executors;
/**
* Google 授权登录 V2 实现类
*
* 基于 Credential Manager 的 Sign-In with Google 方案
* 替代旧版 Google Sign-In SDK
*
* 官方文档:
* - 新版: https://developer.android.com/identity/sign-in/credential-manager-siwg?hl=zh-cn
* - 旧版: https://developer.android.google.cn/identity/legacy/gsi?hl=zh-cn
*
* 参考博客:
* - https://blog.csdn.net/zll18201518375/article/details/138577963
*/
public class TestGoogleSdk {
// ═══════════════════════════════════════════
// 成员变量
// ═══════════════════════════════════════════
/** 凭证请求对象 */
GetCredentialRequest getCredentialRequest;
/** CredentialManager 实例 */
CredentialManager credentialManager;
/** 取消信号 */
CancellationSignal cancellationSignal;
/** 日志标签 */
private static final String TAG = "TestGoogleSdk";
/** Google OAuth Web 客户端 ID */
private String clientId = "162951404116-ebkc28baqbt288h3diklgfng7i00l34j.apps.googleusercontent.com";
/** 单例实例 */
private static TestGoogleSdk instance = null;
// ═══════════════════════════════════════════
// 单例模式
// ═══════════════════════════════════════════
/**
* 私有构造方法
*/
private TestGoogleSdk() {
// 防止外部实例化
}
/**
* 获取单例实例(线程安全 - 双重检查锁定)
*
* @return TestGoogleSdk 单例对象
*/
public static TestGoogleSdk getInstance() {
if (instance == null) {
synchronized (TestGoogleSdk.class) {
if (instance == null) {
instance = new TestGoogleSdk();
}
}
}
return instance;
}
// ═══════════════════════════════════════════
// 初始化
// ═══════════════════════════════════════════
/**
* 初始化 CredentialManager
*
* 应在 Activity 的 onCreate 中调用
*
* @param activity 当前 Activity 上下文
*/
public void init(Activity activity) {
try {
credentialManager = CredentialManager.create(activity);
} catch (Exception e) {
LogUtil.e(TAG, "get client id fail," + e.getMessage());
}
}
// ═══════════════════════════════════════════
// 登录
// ═══════════════════════════════════════════
/**
* 发起 Google 登录请求
*
* 调用流程:
* 1. 构建 GetSignInWithGoogleOption(配置 clientId 和 nonce)
* 2. 构建 GetCredentialRequest
* 3. 调用 CredentialManager.getCredentialAsync() 异步获取凭证
* 4. 在回调中处理登录结果
*
* @param activity 当前 Activity 上下文
*/
public void login(Activity activity) {
LogUtil.d(TAG, "google_server_client_id:" + clientId);
// ───────────────────────────────────────
// 方案一: GetGoogleIdOption(底部动作条界面)
// 特点: 已登录 Play 商店的用户体验更流畅
// 缺点: 未登录 Play 商店时不会拉起商店
// ───────────────────────────────────────
/*
GetGoogleIdOption getGoogleIdOption = new GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(clientId) // webID
.setAutoSelectEnabled(true)
.build();
*/
// ───────────────────────────────────────
// 方案二: GetSignInWithGoogleOption(普通登录界面)
// 特点: 标准 Google 登录弹窗,兼容性更好
// 推荐: 通用场景,未登录 Play 商店也能使用
// ───────────────────────────────────────
String nonce = "11121";
// 生产环境建议: EncodeUtil.encodeBase64(UUID.randomUUID().toString());
GetSignInWithGoogleOption signInWithGoogleOption =
new GetSignInWithGoogleOption.Builder(clientId)
.setNonce(nonce)
.build();
// 构建凭证请求
getCredentialRequest = new GetCredentialRequest.Builder()
// .addCredentialOption(getGoogleIdOption) // 底部动作条界面
.addCredentialOption(signInWithGoogleOption) // 普通登录界面
.build();
// 设置取消信号
cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
// 用户取消操作时的回调
});
// 异步获取凭证
credentialManager.getCredentialAsync(
activity,
getCredentialRequest,
cancellationSignal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
@Override
public void onResult(GetCredentialResponse credentialResponse) {
LogUtil.e(TAG, "onResult");
handleResult(credentialResponse, nonce);
}
@Override
public void onError(@NonNull GetCredentialException e) {
LogUtil.e(TAG, "onError:" + e);
if (e instanceof GetCredentialCancellationException) {
LogUtil.e(TAG, "login cancel " + e.getMessage());
} else {
// 其他错误处理
}
}
}
);
}
// ═══════════════════════════════════════════
// 登录结果处理
// ═══════════════════════════════════════════
/**
* 处理登录返回的凭证数据
*
* 解析流程:
* 1. 获取 Credential 对象
* 2. 判断是否为 CustomCredential 类型
* 3. 判断凭证类型是否为 GOOGLE_ID_TOKEN_CREDENTIAL
* 4. 提取 idToken、用户信息
* 5. 构造登录数据传给后端
*
* @param credentialResponse 登录响应对象
* @param nonce 登录时使用的 nonce(用于安全校验)
*/
private void handleResult(GetCredentialResponse credentialResponse, String nonce) {
Credential credential = credentialResponse.getCredential();
if (credential instanceof CustomCredential) {
// 凭证类型: com.google.android.libraries.identity.googleid.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
GoogleIdTokenCredential googleIdTokenCredential = null;
String err = "";
try {
googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.getData());
} catch (Exception e) {
err = e.getMessage();
e.printStackTrace();
}
if (googleIdTokenCredential == null) {
return;
}
// ───────────────────────────────────────
// 提取用户信息
// ───────────────────────────────────────
String idToken = googleIdTokenCredential.getIdToken();
String id = googleIdTokenCredential.getId();
// String accountName = googleIdTokenCredential.getEmail();
String name = googleIdTokenCredential.getGivenName();
String familyName = googleIdTokenCredential.getFamilyName();
LogUtil.d(TAG, "google login success, idToken:" + idToken);
LogUtil.d(TAG, "google login success, id:" + id + ", name:" + name + ", familyName:" + familyName);
// ───────────────────────────────────────
// 构造登录数据
// ───────────────────────────────────────
Map<String, Object> googleloginInfo = new ArrayMap<>();
googleloginInfo.put("google", Collections.singletonMap("token", idToken));
// TODO: 将 idToken 传给后端验证
// authStateListener.onAuthSuccess(loginType(), googleloginInfo);
} else {
String err = "Unexpected type of credential" + credential.getType();
LogUtil.e(TAG, err);
}
} else {
String err = "Unexpected type of credential, className:" + credential.getClass().getName();
LogUtil.e(TAG, err);
}
}
// ═══════════════════════════════════════════
// 登出
// ═══════════════════════════════════════════
/**
* 清除 Google 登录凭证状态
*
* 调用 CredentialManager.clearCredentialStateAsync() 清除本地凭证
*
* @param activity 当前 Activity 上下文
*/
public void logout(Activity activity) {
LogUtil.d(TAG, "do logout");
ClearCredentialStateRequest clearCredentialStateRequest = new ClearCredentialStateRequest();
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
LogUtil.e(TAG, "logout onCancel");
});
if (credentialManager != null) {
credentialManager.clearCredentialStateAsync(
clearCredentialStateRequest,
cancellationSignal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<Void, ClearCredentialException>() {
@Override
public void onResult(Void result) {
LogUtil.d(TAG, "Google logout onSuccess");
}
@Override
public void onError(ClearCredentialException e) {
LogUtil.e(TAG, "logout onError:" + e);
}
}
);
}
}
// ═══════════════════════════════════════════
// 工具方法
// ═══════════════════════════════════════════
/**
* 获取登录类型标识
*
* @return 登录类型常量(GOOGLE)
*/
public int loginType() {
return TestLoginType.GOOGLE;
}
}
四、核心类架构
4.1 类图
┌─────────────────────────────────────┐
│ TestGoogleSdk │
│ ┌─────────────────────────────┐ │
│ │ - instance: TestGoogleSdk │ │
│ │ - credentialManager │ │
│ │ - getCredentialRequest │ │
│ │ - cancellationSignal │ │
│ │ - clientId: String │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ + getInstance() │ │
│ │ + init(Activity) │ │
│ │ + login(Activity) │ │
│ │ + logout(Activity) │ │
│ │ + loginType(): int │ │
│ │ - handleResult(...) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ CredentialManager │
│ (androidx.credentials) │
│ │
│ + create(context) │
│ + getCredentialAsync(...) │
│ + clearCredentialStateAsync(...) │
└─────────────────────────────────────┘
4.2 方法说明
| 方法 | 访问权限 | 说明 |
|---|---|---|
getInstance() | public static | 获取单例实例(线程安全 - 双重检查锁定) |
init(Activity) | public | 初始化 CredentialManager |
login(Activity) | public | 发起 Google 登录请求 |
logout(Activity) | public | 清除登录凭证状态 |
loginType() | public | 返回登录类型标识(GOOGLE) |
handleResult(...) | private | 处理登录返回的凭证数据 |
五、接入步骤详解
5.1 初始化
在 Activity 的 onCreate 中调用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 Google SDK
TestGoogleSdk.getInstance().init(this);
}
内部实现详解:
public void init(Activity activity) {
try {
// 创建 CredentialManager 实例
// 这是 Credential Manager 库的入口类
credentialManager = CredentialManager.create(activity);
} catch (Exception e) {
LogUtil.e(TAG, "get client id fail," + e.getMessage());
}
}
5.2 发起登录
// 点击登录按钮时调用
TestGoogleSdk.getInstance().login(this);
登录流程详解:
Step 1:配置 GetSignInWithGoogleOption
// nonce 用于防止重放攻击,生产环境应动态生成
String nonce = "11121";
// 构建 Sign-In with Google 选项
GetSignInWithGoogleOption signInWithGoogleOption =
new GetSignInWithGoogleOption.Builder(clientId)
.setNonce(nonce)
.build();
参数说明:
clientId: Google Cloud Console 中创建的 Web 应用客户端 ID(格式:xxx.apps.googleusercontent.com)nonce: 随机字符串,用于安全校验,防止重放攻击
Step 2:构建 GetCredentialRequest
getCredentialRequest = new GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build();
Step 3:异步获取凭证
// 创建取消信号(用于用户主动取消操作)
cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
// 取消回调
});
// 异步调用 CredentialManager 获取凭证
credentialManager.getCredentialAsync(
activity, // 当前 Activity
getCredentialRequest, // 凭证请求
cancellationSignal, // 取消信号
Executors.newSingleThreadExecutor(), // 后台线程执行器
new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
@Override
public void onResult(GetCredentialResponse credentialResponse) {
// 登录成功,处理凭证
handleResult(credentialResponse, nonce);
}
@Override
public void onError(@NonNull GetCredentialException e) {
// 登录失败或取消
if (e instanceof GetCredentialCancellationException) {
// 用户取消登录
} else {
// 其他错误(网络异常、配置错误等)
}
}
}
);
5.3 处理登录结果
private void handleResult(GetCredentialResponse credentialResponse, String nonce) {
Credential credential = credentialResponse.getCredential();
if (credential instanceof CustomCredential) {
// 检查凭证类型是否为 Google ID Token
if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
GoogleIdTokenCredential googleIdTokenCredential =
GoogleIdTokenCredential.createFrom(credential.getData());
// 提取关键信息
String idToken = googleIdTokenCredential.getIdToken(); // 用于后端验证的令牌
String id = googleIdTokenCredential.getId(); // 用户唯一标识
String name = googleIdTokenCredential.getGivenName(); // 名字
String familyName = googleIdTokenCredential.getFamilyName(); // 姓氏
// String email = googleIdTokenCredential.getEmail(); // 邮箱
// 构造登录数据传给后端
Map<String, Object> googleloginInfo = new ArrayMap<>();
googleloginInfo.put("google", Collections.singletonMap("token", idToken));
// TODO: 调用后端接口验证 idToken
}
}
}
5.4 登出
// 点击登出按钮时调用
TestGoogleSdk.getInstance().logout(this);
内部实现详解:
public void logout(Activity activity) {
// 创建清除凭证请求
ClearCredentialStateRequest clearCredentialStateRequest = new ClearCredentialStateRequest();
// 创建取消信号
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
LogUtil.e(TAG, "logout onCancel");
});
// 异步清除凭证状态
if (credentialManager != null) {
credentialManager.clearCredentialStateAsync(
clearCredentialStateRequest,
cancellationSignal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<Void, ClearCredentialException>() {
@Override
public void onResult(Void result) {
LogUtil.d(TAG, "Google logout onSuccess");
// 清除成功,更新 UI 状态
}
@Override
public void onError(ClearCredentialException e) {
LogUtil.e(TAG, "logout onError:" + e);
// 清除失败处理
}
}
);
}
}
六、关键配置项
6.1 clientId 配置
private String clientId = "162951404116-ebkc28baqbt288h3diklgfng7i00l34j.apps.googleusercontent.com";
获取方式:
- 登录 Google Cloud Console
- 选择项目 → APIs & Services → Credentials
- 点击 Create Credentials → OAuth 2.0 Client ID
- 选择 Web application 类型
- 配置 Authorized redirect URIs(如需要)
- 复制 Client ID
Google Cloud Console
├── APIs & Services
│ ├── Credentials
│ │ ├── Create Credentials
│ │ │ └── OAuth 2.0 Client ID
│ │ │ └── Application type: Web application
│ │ └── [Your Client ID]
6.2 Nonce 配置
当前代码使用固定 nonce,生产环境强烈建议改为动态生成:
// ❌ 开发环境(不安全)
String nonce = "11121";
// ✅ 生产环境(推荐)
String nonce = Base64.encodeToString(
UUID.randomUUID().toString().getBytes(),
Base64.NO_WRAP
);
Nonce 作用:
- 防止重放攻击
- 确保 idToken 的唯一性
- 服务端可验证 nonce 是否匹配
七、登录方式对比
| 特性 | GetSignInWithGoogleOption | GetGoogleIdOption |
|---|---|---|
| 类名 | GetSignInWithGoogleOption | GetGoogleIdOption |
| 界面 | 标准 Google 登录弹窗 | 底部 Bottom Sheet 动作条 |
| Play 商店依赖 | 不依赖 | 依赖(需已登录) |
| 用户体验 | 传统登录流程 | 更流畅(一键登录) |
| 适用场景 | 通用场景、未登录 Play 商店 | 已登录 Play 商店的用户 |
| 兼容性 | 高 | 中 |
| 本项目使用 | ✅ | ❌ |
选择建议:本项目使用
GetSignInWithGoogleOption,因为底部动作条在未登录 Play 商店时不会拉起商店,兼容性更好。
八、常见问题
Q1: 登录时提示 "No credentials available"
原因:设备上没有可用的 Google 账号
解决:
- 确保设备已添加 Google 账号(设置 → 账号 → 添加账号)
- 检查网络连接
- 确认 clientId 配置正确
Q2: idToken 验证失败
原因:
- nonce 不匹配
- idToken 已过期
- clientId 与 Google Cloud Console 配置不一致
解决:
- 确保前后端使用相同的 nonce
- 及时使用 idToken(有效期约 1 小时)
- 核对 clientId 配置
Q3: AGP 版本不兼容
错误信息:
Dependency 'androidx.credentials:credentials:1.7.0-alpha02'
requires Android Gradle plugin 8.6.0 or higher.
解决:降级到稳定版
implementation "androidx.credentials:credentials:1.2.2"
implementation "androidx.credentials:credentials-play-services-auth:1.2.2"
九、注意事项
-
AGP 版本兼容
androidx.credentials1.2.2 兼容 AGP 8.0.x,如需使用更高版本需同步升级 AGP 和 Gradle。 -
线程处理
登录回调在后台线程执行,更新 UI 需切换到主线程:activity.runOnUiThread(() -> { // 更新 UI }); -
Nonce 安全
生产环境务必使用随机生成的 nonce,防止重放攻击。 -
idToken 验证
获取到的idToken必须传给服务端,通过 Google 公钥验证,不可仅在客户端验证。 -
ProGuard 配置
如开启代码混淆,需添加保留规则:-keep class androidx.credentials.** { *; } -keep class com.google.android.libraries.identity.googleid.** { *; }
十、参考链接
| 资源 | 链接 |
|---|---|
| 官方文档(新版) | developer.android.com/identity/si… |
| 官方文档(旧版) | developer.android.google.cn/identity/le… |
| 参考博客 | blog.csdn.net/zll18201518… |
| Google Cloud Console | console.cloud.google.com/ |
| 项目源码 | TestGoogleSdk.java |
文档版本: V2.0
最后更新: 2026-05-27
维护者: Android 开发团队