最近升级完了谷歌凭据管理器登录想写一下总结,原因是最开始用的谷歌一键登录SDK要被弃用了,我把相关的接入流程记录一下,说说最近踩过的坑,和怎么解决的问题。给后边想接入的朋友们提供一些参考。
如果文章对你有帮助麻烦点个关注,后边会不定期更新给游戏接入各种国内国外SDK的文章。 本文介绍的接入方式是通过安卓原生的方式接入,然后编译成aar给unity项目使用。
目录
游戏和应用接入新版谷歌凭据管理器登录SDK Credential Manager
1.接入前的准备工作
官方文档链接:将凭据管理器与“使用 Google 账号登录”集成 | Android 开发者 | Android Developers
首先在Android项目的build.gradle文件的dependencies中添加最新的依赖具体参考下图
implementation 'androidx.credentials:credentials:1.3.0-alpha01'
implementation 'androidx.credentials:credentials-play-services-auth:1.3.0-alpha01'
implementation"com.google.android.libraries.identity.googleid:googleid:1.1.0"
2.在Java脚本中实现Google登录
(1)先实例化登录请求参数
(2)构建登录请求
(3)登录成功的回调失败的回调
成功回到中可以拿到我们需要的用户的信息如用户ID,名字,头像等
失败回调中处理登录异常比如参数错误导致的找不到匹配的凭据之类的异常
(4)注销登录
通过CredentialManager的clearCredentialStateAsync方法注销登录
3.遇到的问题和注意事项
1.如果调用CredentialManager.create(mContext)报没有找到CredentialManager的静态方法“No static method” 错误的时候,可以在调用CredentialManager.create(mContext);方法的时候改成CredentialManager.Companion.create(mContext);
2.During begin sign in, failure response from one tap: 10: Developer console is not set up correctly. 后台配置问题,也可能是.setFilterByAuthorizedAccounts没设置为false的原因,如果是这个问题的话可以参考文档先将 setFilterByAuthorizedAccounts 参数设置为 true。如果没有可用的凭据 ,请再次调用该 API 并将 setFilterByAuthorizedAccounts 设置为 false,可以在失败方法handleFailure()里进行处理。或者直接设置.setFilterByAuthorizedAccounts(false),文章中就是直接设置为false默认没有凭据也没用重新再调用API设置。
3.During begin sign in, failure response from one tap: 16: Cannot find a matching credential.检查.setServerClientId(SERVER_CLIENT_ID)传入的ID是不是服务器的clientID这个ID在后台叫WEB应用客户端ID
4.为了提高登录安全性并避免重放攻击,使用 setNonce 我这没加需要的可以参考官方文档怎么生成Nonce的部分。
5.打包的时候设置安卓目标SDK版本为34 targetSdkVersion 34 不设置为34打包会报错。
Android设置:
unity设置:
6.如果打包是因为jdk导致的错误,可以改成jdk11再打包。如果安卓编译的时候出现A problem occurred configuring project ':app'.> No such property: buildDependencies for class: org.gradle.api.internal.artifacts.DefaultResolvedArtifact错误的时候多数原因是配置路径 jdk 或者ndk导致的错误,尽可能用自己安装的jdk路径和ndk路径,修改方法:
7.拿返回的Token去服务器验证登录拿到更多信息参考: 如“sub”以前旧版登录拿到的用户IDhttps://cloud.google.com/docs/authentication/token-types?hl=zh-cn#id-contents
8.androidx.credentials.exceptions.NoCredentialException: No credentials available 没有可用凭据,一般报这种错误应该是用的参数不正确要用服务器的clientID,而不是安卓的ClientID,或者是keystore和谷歌后台对不上导致的,就是打包的用的keystore的SHA-1和谷歌后台的SHA-1对不上可以检查一下。
4.完整代码
调用googleLogin();方法直接登录,调用GoogleLoginOut();注销登录,handleSignIn();处理登录成功之后用户信息的获取,handleFailure();处理登录失败的逻辑。
注意:下面完整代码里用到的登录方式是通过底部动作条凭据选择界面进行登录想要使用 Google 账号登录”按钮流程参考下文中的截图
package com.xx.androidsdk.Google;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import com.google.android.libraries.identity.googleid.GetGoogleIdOption;
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
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.PasswordCredential;
import androidx.credentials.PublicKeyCredential;
import androidx.credentials.exceptions.ClearCredentialException;
import androidx.credentials.exceptions.GetCredentialException;
//谷歌凭据管理器登录
public class GoogleCredentialManagerSign {
private static Activity mActivity = null;
private static Context mContext = null;
private static CredentialManager credentialManager;
private static boolean oneTapStatus = false;
public static void init(Activity activity, Context context) {
mActivity = activity;
mContext = context;
}
public void googleLogin() {
credentialManager = CredentialManager.Companion.create(mContext);
GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
//将 setFilterByAuthorizedAccounts 参数设置为 true。如果没有可用的凭据
// ,请再次调用该 API 并将 setFilterByAuthorizedAccounts 设置为 false
.setFilterByAuthorizedAccounts(false)
.setAutoSelectEnabled(true)
.setServerClientId(PositionId.SERVER_CLIENT_ID)
.build();
GetCredentialRequest request = new GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build();
android.os.CancellationSignal cancellationSignal = new android.os.CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
if (oneTapStatus) oneTapStatus = false;
UAMain.unityLog("Preparing credentials with Google was cancelled.");
});
credentialManager.getCredentialAsync(
mActivity,
request,
cancellationSignal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
@Override
public void onResult(GetCredentialResponse result) {
handleSignIn(result);
}
@Override
public void onError(GetCredentialException e) {
UAMain.unityLog("Unexpected type of credential" + e);
handleFailure(e);
}
});
}
public static void handleSignIn(GetCredentialResponse result) {
// Handle the successfully returned credential.
Credential credential = result.getCredential();
if (credential instanceof PublicKeyCredential) {
String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
// Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
} else if (credential instanceof PasswordCredential) {
String username = ((PasswordCredential) credential).getId();
String password = ((PasswordCredential) credential).getPassword();
// Use id and password to send to your server to validate and authenticate
} else if (credential instanceof CustomCredential) {
if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
// Use googleIdTokenCredential and extract id to validate and
// authenticate on your server
GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(((CustomCredential) credential).getData());
String idToken = googleIdTokenCredential.getIdToken();
try {
JSONObject googleLoginInfoReturn = new JSONObject();
googleLoginInfoReturn.put("id", googleIdTokenCredential.getId());
googleLoginInfoReturn.put("name",googleIdTokenCredential.getDisplayName());
googleLoginInfoReturn.put("photo",googleIdTokenCredential.getProfilePictureUri());
googleLoginInfoReturn.put("token",idToken);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
// Catch any unrecognized custom credential type here.
Log.e("GoogleLog", "Unexpected type of credential");
}
} else {
// Catch any unrecognized credential type here.
Log.e("GoogleLog", "Unexpected type of credential");
}
}
@SuppressLint("RestrictedApi")
private void handleFailure(@NonNull GetCredentialException e) {
Logger logger = Logger.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
logger.log(Level.SEVERE, "Error getting (or preparing) credential: " + e);
}
/**
* 注销登录
*/
public void GoogleLoginOut() {
ClearCredentialStateRequest clearCredentialStateRequest = new ClearCredentialStateRequest();
android.os.CancellationSignal cancellationSignal = new android.os.CancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
if (oneTapStatus) oneTapStatus = false;
Log.e("GoogleLog", "Preparing credentials with Google was cancelled.");
});
if (credentialManager != null) {
credentialManager.clearCredentialStateAsync(
clearCredentialStateRequest,
cancellationSignal,
Executors.newSingleThreadExecutor(),
new CredentialManagerCallback<Void, ClearCredentialException>() {
@Override
public void onResult(Void unused) {
Log.e("GoogleLog", "google注销登录成功");
}
@Override
public void onError(@NonNull ClearCredentialException e) {
Log.e("GoogleLog","注销出错"+e);
}
}
);
}
}
}
使用 Google 账号登录”按钮流程
//通过GetSignInWithGoogleOption 方式登录可添加账号的方式
GetSignInWithGoogleOption signInWithGoogleOption = new GetSignInWithGoogleOption.Builder(PositionId.SERVER_CLIENT_ID).build();
GetCredentialRequest request = new GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build();
5.登录效果
1.(通过底部动作条凭据选择界面进行登录)
2.(使用 Google 账号登录”按钮界面可以添加账号也可以跳转到谷歌商店注册)