Google 登录接入文档
一、准备工作
1.1 Google Cloud Console 后台配置
- 前往 Google Cloud Console 创建项目
- 启用 Google Sign-In API
- 在「凭据」中创建 OAuth 2.0 客户端 ID :
- 应用类型选择「Android」
- 填写包名(Package Name)
- 填写 SHA-1 证书指纹
- 获取 Web Client ID (用于
requestIdToken)
1.2 获取 SHA-1 证书指纹
方式一:使用 Gradle 命令
bash
# Windows
gradlew signingReport
# Mac/Linux
./gradlew signingReport
方式二:使用 keytool 命令
bash
# Debug 签名
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# Release 签名
keytool -list -v -keystore your-release-key.jks -alias your-alias
注意: 需要将 Debug 和 Release 的 SHA-1 都配置到 Google Cloud Console。
二、Gradle 依赖配置
在 app/build.gradle 中添加 Google Sign-In SDK 依赖:
groovy
dependencies {
// Google 登录
implementation 'com.google.android.gms:play-services-auth:19.2.0'
}
最新版本依赖(推荐):
groovy
dependencies {
implementation 'com.google.android.gms:play-services-auth:20.7.0'
}
三、AndroidManifest.xml 配置
3.1 添加 Meta-data 配置(可选)
xml
<application>
<!-- Google Client ID(可选,也可在代码中配置) -->
<meta-data
android:name="GOOGLE_CLIENT_ID"
android:value="你的_WEB_CLIENT_ID.apps.googleusercontent.com" />
</application>
示例:
xml
<meta-data
android:name="GOOGLE_CLIENT_ID"
android:value="474284414568-kehn62d39adv885i1pkh4s7tnu98iflq.apps.googleusercontent.com" />
3.2 添加网络权限
xml
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
四、SDK 初始化与登录控制器
4.1 完整控制器实现
java
package com.example.myapplication;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
/**
* Google 登录控制器
*/
public class TestGoogleLoginController {
private static final String TAG = "TestGoogleLoginController";
// Google 登录客户端
private GoogleSignInClient mGoogleSignInClient;
// 请求码
public static final int RC_SIGN_IN = 9001;
// 登录类型常量
public static final int LOGIN_TYPE = 1; // 登录
public static final int BIND_TYPE = 2; // 绑定
private int currentType = 0;
private Dialog dialog;
// 单例
private static TestGoogleLoginController instance = null;
private TestGoogleLoginController() {}
public static TestGoogleLoginController getInstance() {
if (instance == null) {
synchronized (TestGoogleLoginController.class) {
if (instance == null) {
instance = new TestGoogleLoginController();
}
}
}
return instance;
}
/**
* 初始化 Google 登录
* @param context 上下文
*/
public void init(Context context) {
Log.e(TAG, "initGoogleLogin");
try {
// 替换为你的 Web Client ID
String googleClientId = "你的_WEB_CLIENT_ID.apps.googleusercontent.com";
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(googleClientId) // 请求 ID Token
.requestEmail() // 请求邮箱
.build();
mGoogleSignInClient = GoogleSignIn.getClient(context, gso);
} catch (Throwable e) {
Log.e(TAG, "初始化失败: " + e.getMessage());
}
}
/**
* 发起 Google 登录
* @param activity Activity
* @param loginType 登录类型(LOGIN_TYPE 或 BIND_TYPE)
* @param dialog 加载弹窗(可为 null)
*/
public void login(Activity activity, int loginType, Dialog dialog) {
try {
setDialog(dialog);
this.currentType = loginType;
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
activity.startActivityForResult(signInIntent, RC_SIGN_IN);
} catch (Throwable e) {
Log.e(TAG, "登录失败: " + e.getMessage());
}
}
/**
* 处理登录回调(必须在 Activity.onActivityResult 中调用)
* @param data Intent 数据
*/
public void onActivityResult(Intent data) {
try {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
GoogleSignInAccount account = task.getResult(ApiException.class);
if (account != null) {
if (this.currentType == BIND_TYPE) {
// 处理绑定逻辑
handleBind(account);
} else {
// 处理登录逻辑
handleLogin(account);
}
}
} catch (ApiException e) {
Log.e(TAG, "登录异常,状态码: " + e.getStatusCode());
} catch (Throwable e) {
Log.e(TAG, "处理回调异常: " + e.getMessage());
}
}
/**
* 处理登录成功
*/
private void handleLogin(GoogleSignInAccount account) {
String idToken = account.getIdToken();
String displayName = account.getDisplayName();
String email = account.getEmail();
String id = account.getId();
Log.i(TAG, "登录成功");
Log.i(TAG, "ID Token: " + idToken);
Log.i(TAG, "用户名: " + displayName);
Log.i(TAG, "邮箱: " + email);
Log.i(TAG, "用户ID: " + id);
// 将 idToken 发送到服务器进行验证
}
/**
* 处理绑定
*/
private void handleBind(GoogleSignInAccount account) {
String idToken = account.getIdToken();
String displayName = account.getDisplayName();
Log.i(TAG, "绑定成功");
Log.i(TAG, "ID Token: " + idToken);
// 将 idToken 发送到服务器进行绑定
}
/**
* Google 登出
* @param activity Activity
*/
public void logout(Activity activity) {
try {
if (mGoogleSignInClient != null) {
mGoogleSignInClient.signOut().addOnCompleteListener(activity,
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
Log.i(TAG, "登出成功");
}
});
}
} catch (Throwable e) {
Log.e(TAG, "登出异常: " + e.getMessage());
}
}
/**
* 撤销账号访问权限
* @param activity Activity
*/
public void revokeAccess(Activity activity) {
try {
if (mGoogleSignInClient != null) {
mGoogleSignInClient.revokeAccess().addOnCompleteListener(activity,
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
Log.i(TAG, "撤销访问成功");
}
});
}
} catch (Throwable e) {
Log.e(TAG, "撤销访问异常: " + e.getMessage());
}
}
/**
* 检查是否已登录
*/
public boolean isLoggedIn(Context context) {
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(context);
return account != null;
}
/**
* 获取上次登录的账号
*/
public GoogleSignInAccount getLastSignedInAccount(Context context) {
return GoogleSignIn.getLastSignedInAccount(context);
}
public Dialog getDialog() {
return dialog;
}
public void setDialog(Dialog dialog) {
this.dialog = dialog;
}
}
五、Activity 中集成
5.1 初始化
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 Google 登录控制器
TestGoogleLoginController.getInstance().init(this);
}
}
5.2 处理回调(必需)
java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Google 登录回调
if (requestCode == TestGoogleLoginController.RC_SIGN_IN) {
TestGoogleLoginController.getInstance().onActivityResult(data);
}
}
5.3 登录调用
java
// 登录
btnGoogleLogin.setOnClickListener(v -> {
TestGoogleLoginController.getInstance().login(
this,
TestGoogleLoginController.LOGIN_TYPE,
null
);
});
// 绑定
btnGoogleBind.setOnClickListener(v -> {
TestGoogleLoginController.getInstance().login(
this,
TestGoogleLoginController.BIND_TYPE,
null
);
});
// 登出
btnGoogleLogout.setOnClickListener(v -> {
TestGoogleLoginController.getInstance().logout(this);
});
六、获取用户信息
登录成功后可获取的用户信息:
java
public void getUserInfo(GoogleSignInAccount account) {
// 用户 ID
String id = account.getId();
// 显示名称
String displayName = account.getDisplayName();
// 名字
String givenName = account.getGivenName();
// 姓氏
String familyName = account.getFamilyName();
// 邮箱
String email = account.getEmail();
// 头像 URL
Uri photoUrl = account.getPhotoUrl();
// ID Token(用于服务器验证)
String idToken = account.getIdToken();
Log.i(TAG, "用户ID: " + id);
Log.i(TAG, "显示名称: " + displayName);
Log.i(TAG, "邮箱: " + email);
Log.i(TAG, "头像: " + photoUrl);
Log.i(TAG, "ID Token: " + idToken);
}
七、服务器端验证
7.1 验证 ID Token
将客户端获取的 idToken 发送到服务器,服务器使用 Google API 验证:
Node.js 示例:
javascript
const {OAuth2Client} = require('google-auth-library');
const client = new OAuth2Client(CLIENT_ID);
async function verify(token) {
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
// 验证成功,返回用户信息
return payload;
}
Java 示例:
java
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Collections.singletonList(CLIENT_ID))
.build();
GoogleIdToken idToken = verifier.verify(tokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
String userId = payload.getSubject();
String email = payload.getEmail();
// 验证成功
}
八、错误码说明
| 状态码 | 常量 | 说明 |
|---|---|---|
| 7 | NETWORK_ERROR |
网络错误 |
| 10 | DEVELOPER_ERROR |
配置错误(SHA-1 或包名不匹配) |
| 12500 | SIGN_IN_CANCELLED |
用户取消登录 |
| 12501 | SIGN_IN_CURRENTLY_IN_PROGRESS |
登录正在进行中 |
| 12502 | SIGN_IN_FAILED |
登录失败 |
九、常见问题
Q1: 错误码 10 (DEVELOPER_ERROR)
原因:SHA-1 证书指纹或包名配置错误
解决方案:
- 确保 Google Cloud Console 中配置的包名与
build.gradle中的applicationId一致 - 确保 SHA-1 指纹正确(Debug 和 Release 都需要配置)
- 使用
requestIdToken()时必须使用 Web Client ID,不是 Android Client ID
Q2: 无法获取 ID Token
原因 :未正确配置 requestIdToken()
解决方案:
java
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(WEB_CLIENT_ID) // 必须是 Web Client ID
.requestEmail()
.build();
Q3: 登录后立即显示已登出
原因:可能是模拟器或 Google Play Services 版本问题
解决方案:
- 使用真机测试
- 更新 Google Play Services 到最新版本
Q4: 混淆配置
在 proguard-rules.pro 中添加:
proguard
# Google Play Services
-keep class com.google.android.gms.** { *; }
-keep class com.google.android.gms.auth.** { *; }
-keepattributes Signature
Q5: 静默登录(免登录)
使用 silentSignIn() 实现静默登录:
java
public void silentSignIn(Context context) {
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(context);
if (account != null) {
// 已有登录记录,直接使用
handleLogin(account);
} else {
// 尝试静默登录
mGoogleSignInClient.silentSignIn()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
GoogleSignInAccount signInAccount = task.getResult();
handleLogin(signInAccount);
} else {
// 静默登录失败,需要用户手动登录
}
});
}
}