在穿戴式设备上进行身份验证

手表的屏幕尺寸较小,输入功能较少,这限制了 Wear OS 应用可以使用的身份验证选项。此外,Wear OS 应用可以独立运行,无需配套应用。这意味着 Wear OS 应用需要在从互联网访问数据时自行管理身份验证。

访客模式

不需要进行身份验证即可使用所有功能。相反,它会为用户提供尽可能多的功能,而不要求他们登录。

用户可能会在以前没有使用过移动应用的情况下发现并安装您的 Wear 应用,因此他们可能没有帐号。在这种情况下,访客模式的功能应准确地展示您的应用的功能,以诱导用户创建帐号。

建议的身份验证方法

使用以下身份验证方法,使独立 Wear OS 应用能够获取用户身份验证凭据:

使用 Data Layer 传递令牌

手机上的配套应用可以使用 Wearable Data Layer 将身份验证数据安全地传输到 Wear OS 应用。凭据可以消息或数据项形式传输。

这种类型的身份验证通常不需要用户执行任何操作。不过,请避免在没有通知用户其正在登录的情况下执行身份验证。您可以使用可关闭的简单屏幕通知用户,向他们显示正在从移动设备传输他们的帐号凭据。

重要提示:您的 Wear 应用必须至少提供另外一种身份验证方法,因为此选项仅适用于安装了相应移动应用且与 Android 设备配对的手表。应为没有安装相应移动应用或 Wear OS 设备与 iOS 设备配对的用户提供备用身份验证方法。

使用移动应用中的 Data Layer 传递令牌,如以下示例所示:

val token = "..." // auth token to transmit to the wearable device
val dataClient: DataClient = Wearable.getDataClient(context)
val putDataReq: PutDataRequest = PutDataMapRequest.create("/auth").run {
    dataMap.putString("token", token)
    asPutDataRequest()
}
val putDataTask: Task<DataItem> = dataClient.putDataItem(putDataReq)

在手表应用中监听数据更改事件,如以下示例所示:

val dataClient: DataClient = Wearable.getDataClient(context)
dataClient.addListener{ dataEvents ->
    dataEvents.forEach { event ->
        if (event.type == DataEvent.TYPE_CHANGED) {
            val dataItemPath = event.dataItem.uri.path ?: ""
            if (dataItemPath.startsWith("/auth")) {
                val token = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("token")
                // Display interstitial screen to notify the user they are being signed in.
                // Then, store the token and use it in network requests.
            }
        }
    }
}

如需详细了解如何使用 Wearable Data Layer,请参阅在 Wear OS 上发送和同步数据

使用 OAuth 2.0

Wear OS 支持以下基于 OAuth 2.0 的流程:

  • 采用 PKCE(用于代码交换的证明密钥)的授权代码授权,如 RFC 7636 中所定义。
  • 设备授权,如 RFC 8628 中所定义。

注意:如需确保您的应用在手表进入氛围模式时不会关闭,请在执行身份验证的 activity 中使用 AmbientModeSupport.attach 启用“Always-on”功能。如需详细了解氛围模式下的最佳做法,请参阅让应用始终显示在 Wear 上

用于代码交换的证明密钥 (PKCE)

如需有效地使用 PKCE,请使用 RemoteAuthClient

如需执行从 Wear OS 应用到 OAuth 提供方的身份验证请求,请创建 OAuthRequest 对象。此对象包含指向用于获取令牌的 OAuth 端点的网址和 CodeChallenge 对象。请参阅下方创建身份验证请求的示例:

val request = OAuthRequest.Builder(applicationContext.packageName)
    .setAuthProviderUrl(Uri.parse("https://...."))
    .setCodeChallenge(codeChallenge)
    .build()

构建身份验证请求后,请使用 sendAuthorizationRequest() 方法将其发送到配套应用。

val client = RemoteAuthClient.create(this)
client.sendAuthorizationRequest(request, object : RemoteAuthClient.Callback() {
    override fun onAuthorizationResponse(
        request: OAuthRequest,
        response: OAuthResponse
    ) {
        // Extract the token from the response, store it and use it in network requests.
    }

    override fun onAuthorizationError(errorCode: Int) {
        // Handle error
    }
})

此请求会触发对配套应用的调用,然后,系统会在网络浏览器中向用户的移动设备显示授权界面。OAuth 2.0 提供方会验证用户身份,并请求用户同意所请求的权限。系统会使用您指定的重定向网址将响应发送到您的后端服务器。

授权成功或失败后,OAuth 2.0 服务器会重定向到请求中指定的网址。如果用户批准了访问请求,响应中会包含授权代码。如果用户未批准请求,响应中会包含错误消息。

响应以查询字符串的形式提供,如以下示例所示:

https://example.com/oauthtokens?code=xyz

后端服务器收到授权代码后,可以用该授权代码来换取访问令牌。然后,后端服务器会加载一个页面,可将用户引导至注册为以下形式网址的接收方的 Wear OS 移动应用:

  https://wear.googleapis.com/3p_auth/com.your.package.name?accessToken=abc&refreshToken=xyz

该移动应用会验证响应网址,并使用 onAuthorizationResponse API 将该响应传递给第三方手表应用。

注意:请确保应用软件包名称是重定向网址中的第三个路径组成部分。redirect_uri 必须等于 https://wear.googleapis.com/3p_auth/<receiving app packagename>。 例如 https://wear.googleapis.com/3p_auth/com.package.name

设备授权

使用设备授权时,用户需要在另一台设备上打开验证 URI。然后,授权服务器会要求他们批准或拒绝请求。

为了简化此过程,请使用 RemoteActivityHelper 在用户的配对移动设备上打开网页,如以下示例所示:

// Request access from the authorization server and receive Device Authorization Response
val verificationUri = "..." // extracted from the Device Authorization Response
RemoteActivityHelper.startRemoteActivity(
    this,
    Intent(Intent.ACTION_VIEW)
        .addCategory(Intent.CATEGORY_BROWSABLE)
        .setData(Uri.parse(verificationUri)),
    null
)
// Poll the authorization server to find out if the user completed the user authorization step on their mobile

如果您有 iOS 应用,请使用通用链接在您的应用中拦截此 intent,而不是依赖浏览器向令牌授权。

基于密码的身份验证

如果您的服务器不支持上述任一 OAuth 2.0 流程,请向用户显示用户名和密码提示。

务必要添加自动填充提示,以确保在用户保存了密码的情况下自动向他们提供密码。以下示例在布局文件中的 EditText 上设置了密码提示:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:autofillHints="password" />

如需了解详情,请参阅针对自动填充优化应用

其他身份验证方法

Wear OS 支持其他登录方法。

Google 登录

Google 登录这种方法可让用户使用其现有的 Google 帐号登录。此方法能够提供最佳的用户体验,并且易于提供支持,尤其是您已经在手持式设备应用中实现了这些解决方案时。

Google 登录是最受青睐的解决方案,因为它在 iOS 上也运行良好。下一节将介绍如何完成基本的 Google 登录集成。

前提条件

要将 Google 登录集成到您的 Wear OS 应用中,您必须先配置一个 Google API 控制台项目并设置您的 Android Studio 项目。如需了解详情,请参阅 Google 登录功能集成入门指南。

注意:如果您在与后端服务器通信的应用或网站上使用 Google 登录,请为后端服务器创建一个 OAuth 2.0 Web 应用客户端 ID。此客户端 ID 不同于您的应用的客户端 ID。如需了解详情,请参阅启用服务器端访问

重要提示:如果您的应用与后端服务器通信,则可以使用 HTTPS 发送用户的 ID 令牌,以便在该服务器上安全地识别当前已登录的用户。要了解如何在后端服务器上对用户进行身份验证,请参阅后端服务器身份验证

将 Google 登录集成到您的应用中

如需将 Google 登录集成到您的 Wear OS 应用中,请执行以下操作:

  1. 配置 Google 登录
  2. 添加 Google 登录按钮
  3. 用户点击登录按钮后启动登录流程

配置 Google 登录并构建 GoogleApiClient 对象

在登录 activity 的 onCreate 方法中,将 Google 登录配置为请求应用所需的用户数据。然后,创建一个 GoogleApiClient 对象,并使其可以访问 Google Sign-In API 以及您指定的选项。

Kotlin

// Configure sign-in to request the user's ID, email address, and basic
// profile. The ID and basic profile are included in DEFAULT_SIGN_IN.
// If you need to request additional scopes to access Google APIs, specify them with
// requestScopes().
googleApiClient = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build()
        .let { signInConfigBuilder ->
            // Build a GoogleApiClient with access to the Google Sign-In API and the
            // options specified in the sign-in configuration.
            GoogleApiClient.Builder(this)
                    .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
                    .addApi(Auth.GOOGLE_SIGN_IN_API, signInConfigBuilder)
                    .build()
        }

Java

// Configure sign-in to request the user's ID, email address, and basic
// profile. The ID and basic profile are included in DEFAULT_SIGN_IN.
// If you need to request additional scopes to access Google APIs, specify them with
// requestScopes().
GoogleSignInOptions.Builder signInConfigBuilder = new GoogleSignInOptions
        .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build();

// Build a GoogleApiClient with access to the Google Sign-In API and the
// options specified in the sign-in configuration.
googleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
        .addApi(Auth.GOOGLE_SIGN_IN_API, signInConfigBuilder)
        .build();

在应用中添加 Google 登录按钮

  1. 在应用的布局中添加 SignInButton
  2.  <com.google.android.gms.common.SignInButton
     android:id="@+id/sign_in_button"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content" />
    
  3. 在应用的 onCreate() 方法中,注册按钮的 OnClickListener,以便用户只需点击该按钮即可登录。
  4. Kotlin

    findViewById<View>(R.id.sign_in_button).setOnClickListener(this)
    

    Java

    findViewById(R.id.sign_in_button).setOnClickListener(this);
    

创建登录 intent 并启动登录流程

在用户点击登录按钮时,使用 getSignInIntent 方法创建登录 intent,然后使用 startActivityForResult 方法启动该 intent,以处理 onCLick() 方法中的登录按钮点按操作。

Kotlin

Auth.GoogleSignInApi.getSignInIntent(googleApiClient).also { signInIntent ->
    startActivityForResult(signInIntent, RC_SIGN_IN)
}

Java

Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);

系统会提示用户选择用于登录的 Google 帐号。如果请求访问的范围超出了个人资料、电子邮件和 OpenID,系统还会提示用户授予对这些资源的访问权限。

最后,在 activity 的 onActivityResult 方法中,使用 getSignInResultFromIntent 获取登录结果。获取登录结果后,您可以使用 isSuccess 方法检查登录是否成功。如果登录成功,您可以调用 getSignInAccount 方法来获取 GoogleSignInAccount 对象,该对象包含有关登录用户的信息,如用户名称。

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...)
    if (requestCode == RC_SIGN_IN) {
        Auth.GoogleSignInApi.getSignInResultFromIntent(data)?.apply {
            if (isSuccess) {
                // Get account information
                fullName = signInAccount?.displayName
                mGivenName = signInAccount?.givenName
                mFamilyName = signInAccount?.familyName
                mEmail = signInAccount?.email
            }
        }
    }
}

Java

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...)
    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult signInResult = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        if (signInResult.isSuccess()) {
            GoogleSignInAccount acct = signInResult.getSignInAccount();

            // Get account information
            fullName = acct.getDisplayName();
            givenName = acct.getGivenName();
            familyName = acct.getFamilyName();
            email = acct.getEmail();
        }
    }
}

如需查看实现 Google 登录的示例应用,请参阅 GitHub 上的示例应用

自定义代码身份验证

这种身份验证方法要求用户从外部来源(移动设备/平板电脑/PC)进行身份验证并获取一个短时效代码,然后他们需要在 Wear OS 设备上输入该代码来证明自己的身份,并用该代码换取身份验证令牌。采用这种方法时,您可以通过以下两种方式对使用您的 Wear OS 应用的用户进行身份验证:使用应用的登录模块,或手动将第三方身份验证提供程序的登录方法集成到应用的代码中。虽然这种身份验证方法需要手动操作和额外的工作来提高安全性,但如果您需要在您的独立 Wear OS 应用中更早地进行身份验证,不妨采用这种方法。

此设置的身份验证流程如下:

  1. 用户使用第三方应用执行一项需要授权的操作。
  2. 第三方 Wear OS 应用向用户呈现身份验证屏幕,并指示用户从指定网址输入代码。
  3. 用户切换到移动设备/平板电脑或 PC,启动浏览器,转到 Wear OS 应用上指定的网址,然后登录。
  4. 用户收到一个短时效代码,他们使用 Wear OS 设备自带键盘将其输入到 Wear OS 应用身份验证屏幕,以进行身份验证:

  5. 从此时开始,您可以使用输入的代码作为证据来证明这是正确的用户,并使用该代码来换取在 Wear OS 设备上存储并加密的身份验证令牌,以便后续进行经过身份验证的调用。