APP出海 - google三方登录
1、google平台注册应用
https://console.developers.google.com/auth/clients
选择Android,填入应用包名

填入 SHA-1 证书
平台推荐方式
-
生成密钥
# -keystore 填写密钥的path
# -alias 填写别名
# -keyalg RSA 加密方式
# -keysize 2048 密钥字节长度
# -validity 10000 有效期(天)
keytool -genkey -v -keystore my-release-key.keystore -alias firebase -keyalg RSA -keysize 2048 -validity 10000
-
接下来会提示输入一系列的信息
What is your first and last name?:输入你的名字和姓氏。
What is the name of your organizational unit?:输入你的组织单位名称。
What is the name of your organization?:输入你的组织名称。
What is the name of your City or Locality?:输入你的城市或区 域名称。
What is the name of your State or Province?:输入你的省/市/自治区名称。
What is the two-letter country code for this unit?:输入你的国家/地区代号(两个字母),比如中国为CN。
-
最后提示输入密码,这个密码需要记忆,之后再次验证需要使用。
-
生成之后会将证书签名的内容打印到控制台
密钥库类型: PKCS12
密钥库提供方: SUN
您的密钥库包含 1 个条目
别名: alias_name
创建日期: 2025年3月14日
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: CN=unknow, OU=unknow, O=unknow, L=unknow, ST=unknow, C=unknow
发布者: CN=unknow, OU=unknow, O=unknow, L=unknow, ST=unknow, C=unknow
序列号: 4f91b5521a1c189a
生效时间: Fri Mar 14 15:16:39 CST 2025, 失效时间: Tue Jul 30 15:16:39 CST 2052
证书指纹:
SHA1: 55:5C:CD:79:BA:5D:30:DD:CD:4C:2E:DD:F7:32:DA:E0:FD:C1:D9:8E
SHA256: 48:2B:E1:53:83:1C:02:CF:69:D6:59:2A:BD:EA:4F:5A:93:7C:57:30:B7:F8:05:1A:35:4E:2C:7F:E8:ED:39:95
签名算法名称: SHA384withRSA
主体公共密钥算法: 2048 位 RSA 密钥
版本: 3
扩展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: DD 2A 32 5A 3C E4 BA 60 4C F0 16 87 61 4A 1A 3D .*2Z<..`L...aJ.=
0010: B2 80 CE 61 ...a
]
]
*******************************************
*******************************************
-
生成完成之后可以通过下面命令验证密钥有效性,输入密码之后如果正常输出了密钥内容则是有效的。
keytool -list -v -keystore my-release-key.keystore
-
在module的build.gradle 中配置密钥
signingConfigs {
myConfig {
def hib_key = rootProject.file('hib-applications/hib-app/hib_key')
storeFile hib_key
storePassword 'xxxxx'
keyAlias 'xxx'
keyPassword 'xxxxx'
v2SigningEnabled true
v1SigningEnabled true
}
}
buildTypes {
release {
signingConfig signingConfigs.myConfig
}
debug {
signingConfig signingConfigs.myConfig
}
}
-
将打印内容中的 SHA-1 复制到表格中,如果已经在google商店中进行注册,可以继续验证应用所有权,否则直接点击下方的创建即可
验证方式
可以通过 ./gradlew signingreport 来验证上面的命令生成的证书是否是项目设定的证书,
./gradlew signingreport
> Task :app:signingReport
Variant: debug
Config: debug
Store: /Users/liuqianzhen/.android/debug.keystore
Alias: AndroidDebugKey
MD5: 93:0B:51:CE:C9:9F:BB:D0:69:85:B6:01:40:51:F0:19
SHA1: 4C:D4:50:85:BF:57:94:F4:6A:9C:1A:8C:A4:94:BE:0A:3C:88:7B:F2
SHA-256: F3:EE:09:A6:D5:BF:D5:73:13:4E:A1:63:B3:EB:BE:9E:2E:8F:E2:93:03:D3:06:D7:60:04:B4:75:27:02:CC:8B
Valid until: 2054年8月13日星期四
----------
Variant: release
Config: null
Store: null
Alias: null
----------
Variant: debugAndroidTest
Config: debug
Store: /Users/liuqianzhen/.android/debug.keystore
Alias: AndroidDebugKey
MD5: 93:0B:51:CE:C9:9F:BB:D0:69:85:B6:01:40:51:F0:19
SHA1: 4C:D4:50:85:BF:57:94:F4:6A:9C:1A:8C:A4:94:BE:0A:3C:88:7B:F2
SHA-256: F3:EE:09:A6:D5:BF:D5:73:13:4E:A1:63:B3:EB:BE:9E:2E:8F:E2:93:03:D3:06:D7:60:04:B4:75:27:02:CC:8B
Valid until: 2054年8月13日星期四
----------
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
2、工程中使用
配置依赖
// google 三方登录依赖
implementation "androidx.credentials:credentials:1.2.0"
implementation "androidx.credentials:credentials-play-services-auth:1.2.0"
implementation "com.google.android.libraries.identity.googleid:googleid:1.0.0"
implementation 'com.google.android.gms:play-services-auth:20.4.0'
implementation 'com.google.api-client:google-api-client:1.32.1'
implementation 'com.google.oauth-client:google-oauth-client:1.32.1'
implementation 'com.google.http-client:google-http-client-gson:1.32.1'
implementation 'com.google.http-client:google-http-client-android:1.32.1'
接口调用
-
google三方登录
private suspend fun signIn(context: Context) {
Log.i(TAG, "signIn")
val credentialManager = CredentialManager.create(context)
val nonce = generateNonce()
Log.i(TAG, "signIn nonce: $nonce")
// 这里考虑先试用 isFilterByAuthorizedAccounts = false 可以提供创建新用户的能力
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(CLIENT_ID)
.setAutoSelectEnabled(true)
.setNonce(nonce)
.build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
Log.i(TAG, "signIn connection success")
coroutineScope {
launch {
try {
val result = credentialManager.getCredential(
context, request,
)
Log.i(TAG, "signIn getCredential success")
withContext(Dispatchers.IO) {
handleSignIn(result)
}
} catch (e: Exception) {
handleFailure(e)
}
}
}
}
private fun handleSignIn(result: GetCredentialResponse) {
Log.i(TAG, "handleSignIn")
// Handle the successfully returned credential.
val credential = result.credential
// 初始化 NetHttpTransport 和 JacksonFactory
val transport = NetHttpTransport()
val jsonFactory = AndroidJsonFactory.getDefaultInstance()
var responseJson: String? = null
when (credential) {
// Passkey credential
is PublicKeyCredential -> {
// Share responseJson such as a GetCredentialResponse on your server to
// validate and authenticate
responseJson = credential.authenticationResponseJson
}
// Password credential
is PasswordCredential -> {
// Send ID and password to your server to validate and authenticate.
val username = credential.id
val password = credential.password
}
// GoogleIdToken credential
is CustomCredential -> {
Log.i(TAG, "handleSignIn credential type: ${credential.type}")
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
try {
// Use googleIdTokenCredential and extract the ID to validate and authenticate on your server.
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
val idTokenString = googleIdTokenCredential.idToken
// You can use the members of googleIdTokenCredential directly for UX purposes,
// but don't use them to store or control access to user data.
// For that you first need to validate the token: pass googleIdTokenCredential.getIdToken() to the backend server.
var verifier = GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Collections.singletonList(CLIENT_ID))
.build()
var idToken = verifier.verify(idTokenString)
// To get a stable account identifier (e.g. for storing user data), use the subject ID:
idToken.getPayload().getSubject()
Log.i(TAG, "Received a valid google id token response: " + idToken)
} catch (e: GoogleIdTokenParsingException) {
Log.e(TAG, "Received an invalid google id token response1", e)
} catch (e: Exception) {
Log.e(TAG, "Received an invalid google id token response2", e)
}
} else {
// Catch any unrecognized custom credential type here.
Log.e(TAG, "Unexpected type of credential")
}
}
else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential")
}
}
}
setFilterByAuthorizedAccounts 首先通过调用参数设置为 true 的API,检查用户是否有任何曾经用于登录应用的帐户。用户可以选择可用的帐户进行登录。如果没有可用的授权Google 帐户,需要再次调用 API 并设置setFilterByAuthorizedAccounts 为 false,来提示用户使用其任何可用帐户进行注册。
使用 google 注册之后,用户可以通过设置setAutoSelectEnabled 为 true 选择自动登录
-
设备无google账号,调起登录页面
fun handleFailure(exception: Exception) {
Log.e(TAG, "handleFailure", exception)
// 如果是因为设备上没有登录账号,则触发登录google账户流程
if (exception is NoCredentialException) {
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build()
val googleSignInClient: GoogleSignInClient = GoogleSignIn.getClient(this, gso)
var signInIntent: Intent = googleSignInClient.getSignInIntent()
this.startActivityForResult(signInIntent, RC_SIGN_IN)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(data)
handleSignInResult(task)
}
}
private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
Log.i(TAG, "handleSignInResult: login success ")
// 初始化 NetHttpTransport 和 JacksonFactory
val transport = NetHttpTransport()
val jsonFactory = AndroidJsonFactory.getDefaultInstance()
try {
val account: GoogleSignInAccount = completedTask.getResult(ApiException::class.java)
// 此处可以处理登录成功后的逻辑,使用 account 获取所需的凭据
account?.let{
var idTokenString = it.getIdToken();
var authCode = it.getServerAuthCode();
Log.i(TAG, "Received an id token: " + idTokenString)
// 根据您的需求使用 idToken 或 authCode
// 例如,传递 idToken 到您的后台服务器进行验证
var verifier = GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Collections.singletonList(CLIENT_ID))
.build()
var idToken = verifier.verify(idTokenString)
// To get a stable account identifier (e.g. for storing user data), use the subject ID:
idToken.getPayload().getSubject()
Log.i(TAG, "Received an verifire id token: " + idToken)
}
} catch (e: ApiException) {
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode())
// 处理登录失败的逻辑
}
}
-
退出登录
使用 google 登录之后如果退出登录建议清空会话,便于下次进行登录
fun exit(context: Context) {
Log.i(TAG, "exit")
val credentialManager = CredentialManager.create(context)
lifecycleScope.launch {
val clearCredentialStateRequest = ClearCredentialStateRequest()
credentialManager.clearCredentialState(clearCredentialStateRequest)
}
}
关于请求回来的 googleIdTokenCredential,将其发送到服务端进行验证,再对返回的结果进行处理,https://developers.google.com/identity/gsi/web/guides/verify-google-id-token?hl=zh-cn
3、遇到的问题
问题1:
androidx.credentials.exceptions.NoCredentialException: During begin sign in, failure response from one tap: 16: [28433] Cannot find a matching credential.
原因:设备上未登录google play,登录之后即可解决,如果是这个exception,可以弹一个toast进行提示,或者弹出google登录导引
问题2:
androidx.credentials.exceptions.NoCredentialException: During begin sign in, failure response from one tap: 10: [28444] Developer console is not set up correctly.
原因:google平台上创建 OAuth 2.0 客户端ID 需要使用工程生成的 SHA-1,平台上建议使用 keytool 生成 SHA-1,但是这样生成出来的无法使用,使用 ./gradlew signingreport 生成的 SHA-1,可以配置完成
问题3:
androidx.credentials.exceptions.GetCredentialCancellationException: activity is cancelled by the user.
原因:在 google 平台上创建了 Android OAuth,但是并不能直接使用,需要再次创建一个 Web OAuth,使用 Web ID 进行登录,才可以顺利的调起 google 验证账户登录页面
问题4:
android.os.NetworkOnMainThreadException
原因:网络请求的部分不能在主线程上执行,网络请求一定要放在IO线程上
完整代码:
https://github.com/BugFreeCat/thirdLogin
欢迎各位大佬观赏、评论、留个赞
更多推荐



所有评论(0)