创建自定义账号类型

到目前为止,我们已经讨论了如何访问 Google API,它们使用由 Google 定义的帐号和用户。但是,如果您有自己的在线服务,它并没有 Google 帐号或用户,您该怎么办?事实证明,在用户的设备上安装新的帐号类型相对简单。本课程介绍了如何创建与内置帐号工作原理相同的自定义帐号类型。

实现您的自定义账号代码

首先,您需要使用一种方式从用户那里获取凭据:这可能像要求提供名称和密码的对话框一样简单。也可能是更奇特的过程,例如动态密码或生物识别扫描。无论采用哪种方式,您都需要实现符合以下条件的代码:

  1. 从用户那里收集凭据
  2. 通过服务器验证凭据
  3. 在设备上存储凭据

通常情况下,这三项要求全都可以由一个 Activity 进行处理。我们称之为身份验证器 activity。

由于身份验证器 activity 需要与 AccountManager 系统交互,因此身份验证器 activity 具有普通 activity 所没有的特定要求。为便于处理,Android 框架提供了一个基类 AccountAuthenticatorActivity,您可以扩展该基类来创建自己的自定义身份验证器。

如何满足身份验证器 activity 的前两个要求(凭据收集和身份验证)完全由您决定。(如果只有一种方式可以做到这一点,则不需要使用“自定义”帐号类型。)第三项要求有一个规范且相当简单的实现:

Kotlin

Account(username, your_account_type).also { account ->
    accountManager.addAccountExplicitly(account, password, null)
}

Java

final Account account = new Account(username, your_account_type);
accountManager.addAccountExplicitly(account, password, null);

清楚了解安全注意事项!

请务必注意,AccountManager 不是加密服务或钥匙串。它会以明文形式存储您传递的帐号凭据。在大多数设备上,这并不是一个特别问题,因为它将这些数据存储在一个只有 root 才能访问的数据库中。但在已取得 root 权限的设备上,具有 adb 访问权限的任何人都可以读取这些凭据。

考虑到这一点,您不应将用户的实际密码传递给 AccountManager.addAccountExplicitly(),而是应该存储一个对攻击者用途有限的加密安全令牌。如果您的用户凭据正在保护一些有价值的内容,那么您应该仔细考虑执行类似操作。

注意:对于安全代码,请遵循“Mythbusters”规则:不要在家里尝试这样做!在实现任何自定义帐号代码之前,请咨询安全专家。

现在,安全免责声明已准备就绪,是时候继续工作了。 您已经实现了自定义帐号代码的主要内容;剩下的就是管道连接。

扩展 AbstractAccountAuthenticator

为了让 AccountManager 能与您的自定义帐号代码搭配使用,您需要一个类来实现 AccountManager 所需的接口。这个类便是身份验证器类。

如需创建身份验证器类,最简单的方法是扩展 AbstractAccountAuthenticator 并实现其抽象方法。如果您学习了前面的课程,应该很熟悉 AbstractAccountAuthenticator 的抽象方法:它们与您在上一课中调用的获取帐号信息和授权令牌的方法相反。

正确实现身份验证器类需要许多单独的代码。首先,AbstractAccountAuthenticator 有 7 个您必须替换的抽象方法。其次,您需要将 "android.accounts.AccountAuthenticator"intent 过滤器添加到应用清单中(如下一部分所示)。最后,您必须提供两个 XML 资源,用于定义自定义帐号类型的名称以及系统将显示在此类帐号旁边的图标(以及其他内容)。

如需查看成功实现身份验证器类和 XML 文件的分步指南,请参阅 AbstractAccountAuthenticator 文档。

如果您的身份验证器 activity 需要任何特殊的初始化参数,您可以使用 Intent.putExtra() 将它们附加到 intent。

创建身份验证器服务

现在,您已经有了身份验证器类,接下来需要一个位置来容纳它。帐号身份验证器需要可供多个应用使用并在后台运行,因此它们需要在 Service 内运行。我们将其称为身份验证器服务。

身份验证器服务可以很简单。您只需在 onCreate() 中创建身份验证器类的实例,并在 onBind() 中调用 getIBinder() 即可。

请记得向您的清单文件中添加 <service> 标记,并为 AccountAuthenticator intent 添加 intent 过滤器,并声明帐号身份验证器:

<service ...>
   <intent-filter>
      <action android:name="android.accounts.AccountAuthenticator" />
   </intent-filter>
   <meta-data android:name="android.accounts.AccountAuthenticator"
             android:resource="@xml/authenticator" />
</service>

分发服务

大功告成!系统现在可以识别您的帐号类型,以及“Google”和“Corporate”等所有知名帐号类型。您可以使用帐号和同步设置页面来添加帐号,请求自定义类型的帐号的应用将能够进行枚举和身份验证,就像使用任何其他帐号类型时一样。

当然,所有这些都假定设备上确实安装了您的帐号服务。如果只有一个应用访问该服务,那么没什么大不了的,只需将该服务打包到该应用中即可。但是,如果您希望多个应用使用您的账号服务,情况就会变得更加复杂。您不希望将该服务与所有应用捆绑在一起,也不愿让该服务的多个副本占用用户设备上的空间。

一种解决方案是将该服务安置在一个小的专用 APK 中。当应用想要使用您的自定义帐号类型时,它可以检查设备,查看您的自定义帐号服务是否可用。否则,它可以将用户定向到 Google Play 以下载该服务。这最初看起来可能非常麻烦,但与为每个使用您的自定义帐号的应用重新输入凭据的替代方案相比,它很容易刷新。