如果您支持使用 Google 账号登录,还可以使用一键式登录客户端为用户提供顺畅的账号创建体验,让用户无需离开应用即可完成账号创建。
当您显示一键式登录界面时,系统会提示用户使用设备上的某个 Google 账号在您的应用中创建新账号。如果用户选择继续,您会收到一个包含基本个人资料信息(姓名、个人资料照片和经过验证的电子邮件地址)的 ID 令牌,您可以使用该令牌创建新账号。
实现一键式账号创建有两个部分:
- 将 One Tap 客户端集成到您的应用中,如本页所述。 这与使用一键登录大致相同,但在配置方面存在一些差异。
- 向后端添加使用 Google ID 令牌创建用户账号的功能,如在后端使用 ID 令牌中所述。
在哪些情况下应使用一键注册?
向用户提供一键式注册功能最有效的地方是在登录后可启用新功能的场景中。首先,尝试使用已保存的凭据让用户登录。如果未找到任何已保存的凭据,请提供为用户创建新账号的选项。
准备工作
按照一键式登录使用入门中所述,设置您的 Google API 控制台项目和 Android 项目。
1. 配置 One Tap 客户端
如需配置 One Tap 客户端以创建账号,请执行以下操作:
- 请勿启用密码凭据请求。(只有采用基于令牌的身份验证时,才能实现一键注册。)
使用
setGoogleIdTokenRequestOptions()
和以下设置启用 Google ID 令牌请求:- 将服务器客户端 ID 设置为您在 Google API 控制台中创建的 ID。请注意,这是服务器的客户端 ID,而不是 Android 客户端 ID。
- 将客户端配置为显示设备上的所有 Google 账号,即不按已获授权的账号进行过滤。
- 您还可以选择请求获取该账号的已验证电话号码。
Java
public class YourActivity extends AppCompatActivity { // ... private SignInClient oneTapClient; private BeginSignInRequest signUpRequest; @Override public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); oneTapClient = Identity.getSignInClient(this); signUpRequest = BeginSignInRequest.builder() .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder() .setSupported(true) // Your server's client ID, not your Android client ID. .setServerClientId(getString(R.string.your_web_client_id)) // Show all accounts on the device. .setFilterByAuthorizedAccounts(false) .build()) .build(); // ... } }
Kotlin
class YourActivity : AppCompatActivity() { // ... private lateinit var oneTapClient: SignInClient private lateinit var signUpRequest: BeginSignInRequest override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) oneTapClient = Identity.getSignInClient(this) signUpRequest = BeginSignInRequest.builder() .setGoogleIdTokenRequestOptions( BeginSignInRequest.GoogleIdTokenRequestOptions.builder() .setSupported(true) // Your server's client ID, not your Android client ID. .setServerClientId(getString(R.string.your_web_client_id)) // Show all accounts on the device. .setFilterByAuthorizedAccounts(false) .build()) .build() // ... } // ... }
2. 跟踪一键式界面取消
您应跟踪用户是否已关闭提示或点按提示以外的部分,以便确定用户是否已拒绝使用一键式注册。这可以像 activity 的布尔值属性一样简单。(请参阅下文中的停止显示“一键式”界面。)
3. 显示一键注册界面
如果用户尚未拒绝使用一键式创建新账号,请调用客户端对象的 beginSignIn()
方法,并将监听器附加到它返回的 Task
。当一键式登录请求找不到任何已保存的凭据时,应用通常会执行此步骤,即在登录请求的失败监听器中执行此步骤。
如果用户在设备上设置了一个或多个 Google 账号,One Tap 客户端将调用成功监听器。在成功监听器中,从 Task
结果中获取待处理 intent,并将其传递给 startIntentSenderForResult()
以启动一键式界面。
如果用户在设备上没有任何 Google 账号,One Tap 客户端将调用失败监听器。在这种情况下,您无需采取任何措施:您只需继续呈现应用的未登录体验,用户就可以按照正常的账号创建流程进行注册。
Java
oneTapClient.beginSignIn(signUpRequest)
.addOnSuccessListener(this, new OnSuccessListener<BeginSignInResult>() {
@Override
public void onSuccess(BeginSignInResult result) {
try {
startIntentSenderForResult(
result.getPendingIntent().getIntentSender(), REQ_ONE_TAP,
null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "Couldn't start One Tap UI: " + e.getLocalizedMessage());
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// No Google Accounts found. Just continue presenting the signed-out UI.
Log.d(TAG, e.getLocalizedMessage());
}
});
Kotlin
oneTapClient.beginSignIn(signUpRequest)
.addOnSuccessListener(this) { result ->
try {
startIntentSenderForResult(
result.pendingIntent.intentSender, REQ_ONE_TAP,
null, 0, 0, 0)
} catch (e: IntentSender.SendIntentException) {
Log.e(TAG, "Couldn't start One Tap UI: ${e.localizedMessage}")
}
}
.addOnFailureListener(this) { e ->
// No Google Accounts found. Just continue presenting the signed-out UI.
Log.d(TAG, e.localizedMessage)
}
4. 处理用户的响应
系统会使用 activity 的 onActivityResult()
方法将用户对一键式注册提示的响应报告给您的应用。如果用户选择创建账号,结果将是 Google ID 令牌。如果用户拒绝注册(通过关闭一键式登录界面或点按界面外部),系统会返回代码 RESULT_CANCELED
。您的应用需要处理这两种情况。
使用 Google ID 令牌创建账号
如果用户选择使用 Google 账号注册,您可以将 intent 数据从 onActivityResult()
传递给 One Tap 客户端的 getSignInCredentialFromIntent()
方法,以获取用户的 ID 令牌。凭据将具有非 null 的 googleIdToken
属性。
使用 ID 令牌在后端创建账号(请参阅使用 ID 令牌与后端进行身份验证),然后让用户登录。
该凭据还包含您请求的任何其他详细信息,例如账号的经过验证的电话号码(如果有)。
Java
public class YourActivity extends AppCompatActivity { // ... private static final int REQ_ONE_TAP = 2; // Can be any integer unique to the Activity. private boolean showOneTapUI = true; // ... @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQ_ONE_TAP: try { SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(data); String idToken = credential.getGoogleIdToken(); if (idToken != null) { // Got an ID token from Google. Use it to authenticate // with your backend. Log.d(TAG, "Got ID token."); } } catch (ApiException e) { // ... } break; } } }
Kotlin
class YourActivity : AppCompatActivity() { // ... private val REQ_ONE_TAP = 2 // Can be any integer unique to the Activity private var showOneTapUI = true // ... override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQ_ONE_TAP -> { try { val credential = oneTapClient.getSignInCredentialFromIntent(data) val idToken = credential.googleIdToken when { idToken != null -> { // Got an ID token from Google. Use it to authenticate // with your backend. Log.d(TAG, "Got ID token.") } else -> { // Shouldn't happen. Log.d(TAG, "No ID token!") } } } catch (e: ApiException) { // ... } } } // ... }
停止显示一键登录界面
如果用户拒绝登录,对 getSignInCredentialFromIntent()
的调用将抛出状态代码为 CommonStatusCodes.CANCELED
的 ApiException
。在这种情况下,您应暂时停止显示一键式登录界面,以免因反复提示而让用户感到厌烦。以下示例通过在 activity 上设置一个属性来实现此目的,该属性用于确定是否向用户提供一键式登录;不过,您也可以将值保存到 SharedPreferences
或使用其他方法。
请务必实现您自己的“一键式登录”提示速率限制。如果您不设置此属性,并且用户连续取消多次提示,One Tap 客户端在接下来的 24 小时内将不会向用户显示提示。
Java
public class YourActivity extends AppCompatActivity { // ... private static final int REQ_ONE_TAP = 2; // Can be any integer unique to the Activity. private boolean showOneTapUI = true; // ... @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQ_ONE_TAP: try { // ... } catch (ApiException e) { switch (e.getStatusCode()) { case CommonStatusCodes.CANCELED: Log.d(TAG, "One-tap dialog was closed."); // Don't re-prompt the user. showOneTapUI = false; break; case CommonStatusCodes.NETWORK_ERROR: Log.d(TAG, "One-tap encountered a network error."); // Try again or just ignore. break; default: Log.d(TAG, "Couldn't get credential from result." + e.getLocalizedMessage()); break; } } break; } } }
Kotlin
class YourActivity : AppCompatActivity() { // ... private val REQ_ONE_TAP = 2 // Can be any integer unique to the Activity private var showOneTapUI = true // ... override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQ_ONE_TAP -> { try { // ... } catch (e: ApiException) { when (e.statusCode) { CommonStatusCodes.CANCELED -> { Log.d(TAG, "One-tap dialog was closed.") // Don't re-prompt the user. showOneTapUI = false } CommonStatusCodes.NETWORK_ERROR -> { Log.d(TAG, "One-tap encountered a network error.") // Try again or just ignore. } else -> { Log.d(TAG, "Couldn't get credential from result." + " (${e.localizedMessage})") } } } } } } // ... }
后续步骤
当用户完成一键式注册流程后,您会收到一个 Google ID 令牌,其中包含一些基本个人资料信息:用户的电子邮件地址、全名和个人资料照片网址。对于许多应用,这些信息足以在后端对用户进行身份验证并创建新账号。
如果您需要用户提供其他信息才能完成账号创建(例如用户的出生日期),请向用户显示注册详情流程,在其中请求用户提供这些额外信息。然后,将这些信息发送到后端以完成账号创建。