如果您支持使用 Google 账号登录,则可以使用一键登录客户端,为用户提供顺畅的账号创建体验,而无需离开应用。
当您显示一键快捷登录界面时,系统会提示用户使用其设备上的某个 Google 账号在您的应用中创建一个新账号。如果用户选择继续,您会获得一个包含基本个人资料信息(姓名、个人资料照片和经过验证的邮箱)的 ID 令牌,您可以使用这些信息创建新账号。
实现一键式账号创建功能分为两个部分:
- 将一键登录客户端集成到您的应用中,如本页所述。 这与使用一键登录基本相同,但在配置上有一些差异。
- 向后端添加从 Google ID 令牌创建用户账号的功能,在后端使用 ID 令牌中对此进行了讨论。
在哪些情况下应使用一键注册?
向用户提供一键注册功能的最有效位置是在登录后会启用新功能的上下文中。首先,尝试使用已保存的凭据让用户登录。如果未找到已保存的凭据,请为用户创建新账号。
准备工作
按照一键登录功能使用入门中的说明设置 Google Cloud 控制台项目和 Android 项目。
1. 配置一键登录客户端
如需配置一键式客户端以创建账号,请执行以下操作:
- 不启用密码凭据请求。(只有基于令牌的身份验证才能实现一键注册。)
使用
setGoogleIdTokenRequestOptions()和以下设置启用 Google ID 令牌请求:- 将服务器客户端 ID 设置为您在 Google Cloud 控制台中创建的 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 账号,一键快捷登录客户端会调用成功监听器。在成功监听器中,从
Task结果中获取待处理 intent,并将其传递给startIntentSenderForResult()以启动一键登录界面。 失败监听器:如果用户在设备上没有任何 Google 账号,一键快捷登录客户端会调用失败监听器。在这种情况下,您无需执行任何操作:继续呈现应用的退出登录体验,用户可以使用您的常规账号创建流程进行注册。
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 账号注册,您可以通过将 onActivityResult() 中的 intent 数据传递给一键快捷登录客户端的 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() 的调用将抛出 ApiException,并返回 CommonStatusCodes.CANCELED 状态代码。发生这种情况时,您应暂时停止显示一键登录界面,以免重复提示让用户感到厌烦。以下示例通过在 Activity 上设置一个属性来实现此目的,该属性用于确定是否为用户提供一键登录功能;不过,您也可以将值保存到 SharedPreferences 或使用其他方法。
请务必自行实现一键登录提示的速率限制。 如果您不这样做,并且用户连续取消了多个提示,则一键快捷登录客户端在接下来的 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 令牌,其中包含一些基本个人资料信息:用户的电子邮件地址、全名和个人资料照片网址。对于许多应用,这些信息足以让您在后端对用户进行身份验证并创建新账号。
如果您需要其他信息来完成账号创建(例如用户的出生日期),请向用户显示注册详情流程,以便请求这些额外信息。然后,将这些信息发送到您的后端以完成账号创建。