警告:此数据是根据Google用户数据政策提供的。请查看并遵守政策。否则可能会导致项目暂停或帐户暂停。

使用保存的凭据登录用户

使用 One Tap 登录客户端请求用户许可以检索他们之前用于登录您的应用程序的凭据之一。这些凭据可以是 Google 帐户,也可以是他们使用 Chrome、Android 自动填充或 Smart Lock for Passwords 与 Google 一起保存的用户名密码组合。

一键登录界面

成功检索凭据后,您可以使用它们让用户轻松登录到您的应用程序。

如果用户未保存任何凭据,则不会显示 UI,您可以提供正常的注销体验。

我应该在哪里使用一键登录?

如果您的应用需要用户登录,请在登录屏幕上显示一键式 UI。即使您已经有“使用 Google 登录”按钮,这也很有用:因为可以将 One Tap UI 配置为仅显示用户以前用于登录的凭据,因此它可以提醒不经常登录的用户他们上次登录的方式,并防止他们意外地使用您的应用创建新帐户。

如果您的应用程序可选择登录,请考虑在通过登录增强体验的任何屏幕上使用一键登录。例如,如果用户在退出时可以使用您的应用程序浏览内容,但只能发表评论或登录后将商品添加到购物车,这将是 One Tap 登录的合理上下文。

出于上述原因,登录可选应用程序也应在其登录屏幕上使用一键登录。

在你开始之前

1.配置一键登录客户端

您可以将 One Tap 登录客户端配置为使用保存的密码、保存的 Google 帐户或两者之一登录用户。 (建议同时支持,以便为新用户启用一键式帐户创建,并为尽可能多的回访用户启用自动或一键式登录。)

如果应用程序使用基于口令的登录,使用setPasswordRequestOptions()启用密码凭证请求。

如果应用程序使用谷歌帐户登录,使用setGoogleIdTokenRequestOptions()来启用和配置谷歌ID令牌请求:

  • 设置服务器的客户端ID到您在谷歌API控制台创建的ID 。请注意,这是您服务器的客户端 ID,而不是您的 Android 客户端 ID。

  • 将客户端配置为按授权帐户过滤。当您启用此选项时,One Tap 客户端只会提示用户使用他们过去已经使用过的 Google 帐户登录您的应用。这样做可以帮助用户在不确定自己是否已有帐户或使用的是哪个 Google 帐户时成功登录,并防止用户意外使用您的应用创建新帐户。

  • 如果你想在可能时自动登录的用户,能够与功能setAutoSelectEnabled()当满足以下条件时,可以自动登录:

    • 用户只为您的应用保存了一个凭据。即,一个已保存的密码或一个已保存的 Google 帐户。
    • 用户没有禁用自动登录他们的谷歌帐户设置
  • 虽然可选,但我们强烈建议您强烈考虑使用随机数来提高登录安全性并避免重放攻击。使用setNonce包括在每个请求的随机数。见安全网是获得一个随机数的建议部分,更多细节上产生一个随机数。

爪哇

public class YourActivity extends AppCompatActivity {
  // ...

  private SignInClient oneTapClient;
  private BeginSignInRequest signInRequest;

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState,
                       @Nullable PersistableBundle persistentState) {
      super.onCreate(savedInstanceState, persistentState);

      oneTapClient = Identity.getSignInClient(this);
      signInRequest = BeginSignInRequest.builder()
              .setPasswordRequestOptions(PasswordRequestOptions.builder()
                      .setSupported(true)
                      .build())
              .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                      .setSupported(true)
                      // Your server's client ID, not your Android client ID.
                      .setServerClientId(getString(R.string.default_web_client_id))
                      // Only show accounts previously used to sign in.
                      .setFilterByAuthorizedAccounts(true)
                      .build())
              // Automatically sign in when exactly one credential is retrieved.
              .setAutoSelectEnabled(true)
              .build();
      // ...
  }
  // ...
}

科特林

class YourActivity : AppCompatActivity() {
    // ...

    private lateinit var oneTapClient: SignInClient
    private lateinit var signInRequest: BeginSignInRequest

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        oneTapClient = Identity.getSignInClient(this)
        signInRequest = BeginSignInRequest.builder()
            .setPasswordRequestOptions(BeginSignInRequest.PasswordRequestOptions.builder()
                .setSupported(true)
                .build())
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    // Your server's client ID, not your Android client ID.
                    .setServerClientId(getString(R.string.your_web_client_id))
                    // Only show accounts previously used to sign in.
                    .setFilterByAuthorizedAccounts(true)
                    .build())
            // Automatically sign in when exactly one credential is retrieved.
            .setAutoSelectEnabled(true)
            .build()
        // ...
    }
    // ...
}

2. 检查登录用户

如果您的 Activity 可供登录用户或注销用户使用,请在显示 One Tap 登录 UI 之前检查用户的状态。

您还应该通过关闭提示或在提示之外点击来跟踪用户是否已经拒绝使用一键登录。这可以像您的 Activity 的布尔属性一样简单。 (参见停止显示所述一个抽头UI ,下文)。

3. 显示一键登录界面

如果用户没有登录且尚未下降到使用一个水龙头登录,在客户端调用对象的beginSignIn()方法,并附加听众的Task返回。应用通常为此在活动的onCreate()方法或屏幕转换之后使用单活动架构时。

如果用户为您的应用程序保存了任何凭据,则 One Tap 客户端将调用成功侦听器。在成功的倾听者,从一开始挂起意图Task结果,并把它传递给startIntentSenderForResult()来启动一个点击登录界面。

如果用户没有任何保存的凭据,One Tap 客户端将调用失败侦听器。在这种情况下,不需要任何操作:您只需继续展示应用程序的注销体验即可。但是,如果您支持一键注册,则可以从此处开始该流程以获得无缝的帐户创建体验。见有一个水龙头创建新帐户

爪哇

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 saved credentials found. Launch the One Tap sign-up flow, or
                // do nothing and continue presenting the signed-out UI.
                Log.d(TAG, e.getLocalizedMessage());
            }
        });

科特林

oneTapClient.beginSignIn(signInRequest)
    .addOnSuccessListener(this) { result ->
        try {
            startIntentSenderForResult(
                result.pendingIntent.intentSender, REQ_ONE_TAP,
                null, 0, 0, 0, null)
        } catch (e: IntentSender.SendIntentException) {
            Log.e(TAG, "Couldn't start One Tap UI: ${e.localizedMessage}")
        }
    }
    .addOnFailureListener(this) { e ->
        // No saved credentials found. Launch the One Tap sign-up flow, or
        // do nothing and continue presenting the signed-out UI.
        Log.d(TAG, e.localizedMessage)
    }

4. 处理用户的响应

该用户对一个抽头登录提示响应将使用您的活动的报告给你的应用程序onActivityResult()方法。如果用户选择登录,结果将是保存的凭据。如果用户拒绝登录,或者通过关闭一个抽头UI或敲击外面,结果将与代码返回RESULT_CANCELED 。您的应用程序需要处理这两种可能性。

使用检索到的凭据登录

如果用户选择共享凭据与您的应用程序,你可以通过传递意图数据检索它们onActivityResult()的一个抽头客户getSignInCredentialFromIntent()方法。凭证将有一个非空googleIdToken财产,如果用户共享一个谷歌帐户凭据与您的应用程序,或一个非空password财产,如果用户共享保存的密码。

使用凭据向您的应用后端进行身份验证。

  • 如果检索到用户名和密码对,请使用它们以与用户手动提供它们时相同的方式进行登录。
  • 如果检索到 Google 帐户凭据,请使用 ID 令牌向您的后端进行身份验证。如果您选择使用随机数来帮助避免重放攻击,请检查后端服务器上的响应值。见身份验证使用ID令牌的后端

爪哇

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();
                  String username = credential.getId();
                  String password = credential.getPassword();
                  if (idToken !=  null) {
                      // Got an ID token from Google. Use it to authenticate
                      // with your backend.
                      Log.d(TAG, "Got ID token.");
                  } else if (password != null) {
                      // Got a saved username and password. Use them to authenticate
                      // with your backend.
                      Log.d(TAG, "Got password.");
                  }
              } catch (ApiException e) {
                  // ...
              }
              break;
      }
  }
}

科特林

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
                    val username = credential.id
                    val password = credential.password
                    when {
                        idToken != null -> {
                            // Got an ID token from Google. Use it to authenticate
                            // with your backend.
                            Log.d(TAG, "Got ID token.")
                        }
                        password != null -> {
                            // Got a saved username and password. Use them to authenticate
                            // with your backend.
                            Log.d(TAG, "Got password.")
                        }
                        else -> {
                            // Shouldn't happen.
                            Log.d(TAG, "No ID token or password!")
                        }
                    }
                } catch (e: ApiException) {
                    // ...
                }
            }
        }
    }
    // ...
}

停止显示一键式 UI

如果用户拒绝登录,调用getSignInCredentialFromIntent()将抛出ApiExceptionCommonStatusCodes.CANCELED状态代码。发生这种情况时,您应该暂时禁用一键登录 UI,以免重复提示惹恼您的用户。以下示例通过在 Activity 上设置一个属性来实现此目的,该属性用于确定是否向用户提供“一键登录”;但是,你也可以保存一个值SharedPreferences或使用其他一些方法。

实施您自己的一键登录提示速率限制非常重要。如果您不这样做,并且用户连续取消了多个提示,则 One Tap 客户端将不会在接下来的 24 小时内提示用户。

爪哇

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;
      }
  }
}

科特林

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})")
                        }
                    }
                }
            }
        }
    }
    // ...
}

5. 办理登出

当用户退出你的应用程序中,调用一个抽头客户signOut()方法。调用signOut()禁用自动登录,直至用户再次上线的迹象。

即使您不使用自动登录,此步骤也很重要,因为它可以确保当用户退出您的应用时,您使用的任何 Play 服务 API 的身份验证状态也会重置。

下一步

如果您将 One Tap 客户端配置为检索 Google 凭据,您的应用程序现在可以获取代表您用户的 Google 帐户的 Google ID 令牌。了解如何在后端使用这些标记

如果支持谷歌帐户登录,您也可以用一个水龙头客户端添加摩擦的帐户创建流向你的应用程序