返回

Android凭据共享:解决跨应用密码同步难题

Android

Android凭据管理器应用间密码共享

应用间共享用户凭据(例如用户名和密码)的需求,在某些情况下颇为常见,例如同一服务商的不同客户端应用。Android凭据管理器提供了一套机制来实现这种共享。但开发者在实践过程中可能会遇到问题,原因通常在于配置不当。 本文将分析凭据共享过程中的一些常见问题,并给出相应的解决方案。

问题分析

要使多个应用之间能够共享凭据,需要在以下几个方面进行正确的设置:

  1. assetlinks.json 文件 : 必须在服务器上托管 assetlinks.json 文件,其中包含应用之间的关系声明。
  2. 应用签名 : 应用的签名指纹(SHA256)必须与 assetlinks.json 文件中声明的签名指纹匹配。
  3. asset_statements : 每个应用需要在其 manifest 文件中指定 asset_statements 元数据,并引用 assetlinks.json 文件的路径。
  4. Intent Filter : 应用需要一个合适的 Intent Filter,允许系统验证 assetlinks.json
  5. 凭据管理API调用 : 应用需要使用 Android 提供的凭据管理 API 正确地读取和保存凭据。

如果以上任何一个环节存在问题,凭据共享功能就会失效。

解决方案

检查 assetlinks.json 文件

  • 问题: assetlinks.json 文件配置不正确或服务器托管问题,会导致应用无法正确识别彼此的关联。

  • 解决方案:

    • 确保 assetlinks.json 文件格式正确,且所有 target 中的 package_namesha256_cert_fingerprints 与实际应用的完全匹配。 特别是 sha256_cert_fingerprints, 如果是 debug 应用,要使用 debug 的 key, 发布的应用使用发布 key。
    • relation 字段必须设置为 delegate_permission/common.get_login_creds, 否则凭据共享将无法正常运作。
    • 检查托管 assetlinks.json 文件的 URL 可通过 HTTPS 访问,并且文件放置在 .well-known 目录下。
  • 示例 : 一个正确的 assetlinks.json 文件如下:

    [
      {
        "relation": [
          "delegate_permission/common.get_login_creds"
        ],
        "target": {
          "namespace": "android_app",
          "package_name": "com.practice.app1",
          "sha256_cert_fingerprints": [
            "B0:99:5C:C8:07:12:8E:62:D6:99:D7:54:B2:FC:D8:AA:F4:A2:13:94:A8:38:22:D1:D3:40:59:5E:16:C3:69:8F"
          ]
        }
      },
        {
            "relation": [
                "delegate_permission/common.get_login_creds"
            ],
            "target": {
                "namespace": "android_app",
                "package_name": "com.practice.app2",
                "sha256_cert_fingerprints": [
                    "B0:99:5C:C8:07:12:8E:62:D6:99:D7:54:B2:FC:D8:AA:F4:A2:13:94:A8:38:22:D1:D3:40:59:5E:16:C3:69:8F"
                ]
            }
      }
    ]
    
  • 操作步骤

    1. 获取应用的 SHA256 指纹。 可以通过 keytool 命令来获取 (例如: keytool -printcert -v -file "path/to/your/keystore.jks")。
    2. 更新 assetlinks.json 的对应 sha256_cert_fingerprintspackage_name
    3. 确保 assetlinks.json 能通过https协议访问到。

验证应用签名

  • 问题 : 应用的签名指纹和 assetlinks.json 中定义的指纹不匹配会导致验证失败。
  • 解决方案: 确保应用的 SHA256 证书指纹与 assetlinks.json 文件中记录的指纹完全一致。调试构建(debug)与发布构建(release)使用的签名可能不同。
  • 操作步骤 :
  1. 使用 keytool 命令查看应用程序签名证书的 SHA256 指纹,如上一部分所述。
  2. 确认所生成的指纹是否与 assetlinks.json 中使用的指纹一致。如不一致,更新 assetlinks.json 或者用对应的证书签名app.

配置 asset_statements

  • 问题 : asset_statements 配置错误,可能导致系统无法验证凭据的关联。

  • 解决方案:

    • 确保 asset_statements 的值是一个有效的 JSON 字符串,包含 include 字段,指向 assetlinks.json 文件的 HTTPS 地址。
    • 在 Android Manifest 中声明 asset_statements 元数据:
    <meta-data
    android:name="asset_statements"
    android:resource="@string/asset_statements"/>
    
    • 确保 strings.xml 文件中配置了 asset_statements
     <string name="asset_statements" translatable="false">
        [{
            \"include\": \"https://<git-hub-name>.github.io/.well-known/assetlinks.json\"
         }]
     </string>
    
  • 操作步骤

  1. 在 app 的 strings.xml 文件中添加 asset_statements string资源,务必填写正确的 url, include 指向托管的 assetlinks.json 地址。
  2. 在 Manifest 文件中注册这个 resource, 上面的示例代码中给出。

设置 Intent Filter

  • 问题 : Intent filter 配置不正确,会导致系统无法自动验证 assetlinks.json,或者在验证过程中发生错误。

  • 解决方案 :

    • 使用 android:autoVerify="true" 以便在安装应用程序时尝试验证 assetlinks.json 文件,减少验证步骤,优化用户体验。
    • 配置带有 android.intent.action.VIEW, android.intent.category.DEFAULT, android.intent.category.BROWSABLE 这些 action 和 category 的 intent filter, 并指定 httphttps 以及 assetlinks.json 所在的域名。
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
    
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
    
        <data android:scheme="http" />
        <data android:scheme="https" />
        <data android:host="<git-hub-name>.github.io" />
    </intent-filter>
    
  • 操作步骤

  1. 在每个需要参与共享凭据的应用的 AndroidManifest.xml 中添加 Intent Filter, 并修改 <data> 中的 host 部分以匹配你 assetlinks.json 的地址。

凭据API的使用

  • 问题 : 使用凭据管理API时,没有正确设置请求或者处理响应,也会造成共享失败。

  • 解决方案:

  • 确保通过 CredentialManager.create() 方法获取 CredentialManager 实例。

  • 创建 GetCredentialRequest 时,必须指定密码凭据类型:GetPasswordOption()

  • 妥善处理请求的各类异常。

  • 使用正确的凭据ID从凭据管理器获取数据:凭据保存时的凭据ID就是 PasswordCredential 的 ID 属性。

  • 示例代码:

       private val credentialManager = CredentialManager.create(activity)
    
       suspend fun signIn(): SignInResult {
           return try {
               val credentialResponse = credentialManager.getCredential(
                   context = activity,
                   request = GetCredentialRequest(
                       credentialOptions = listOf(GetPasswordOption())
                   )
               )
    
               val credential = credentialResponse.credential as? PasswordCredential
               ?: return SignInResult.Failure
    
                  //使用credential.id和credential.password进行登录操作
    
               SignInResult.Success(credential.id)
    
            } catch(e: GetCredentialCancellationException) {
                e.printStackTrace()
               SignInResult.Cancelled
           } catch(e: NoCredentialException) {
               e.printStackTrace()
               SignInResult.NoCredentials
           } catch(e: GetCredentialException) {
               e.printStackTrace()
                SignInResult.Failure
           }
      }
    
    
  • 操作步骤 :

    1. 确保已经在你的app module 添加 credential-manager 的依赖: implementation("androidx.credentials:credentials:1.0.0-alpha02")
    2. 参照上面的kotlin代码, 使用credentialManager 来访问存储在手机中的凭据.

其他建议

  • 考虑数据安全:在应用之间共享敏感信息时,应该充分考虑数据的加密存储和传输,避免数据泄露。可以使用 KeyStore 系统来加密保存数据。
  • 测试方法:建议使用 adb 工具来进行关联测试: adb shell pm get-app-links com.example.app. 在没有配置 autoVerify="true" 时,系统会调用 https://<git-hub-name>.github.io/.well-known/assetlinks.json 去检查应用之间的联系。

遵循以上方案,可有效解决 Android 凭据管理器应用间密码共享的问题。正确的配置不仅能够提高开发效率,也能改善用户的使用体验,提升应用的整体安全级别。