5

语境

我的应用程序只存储用户/通行证。不使用令牌。

问题 1

方法setAccountAuthenticatorResult(Bundle)onResult(Bundle)旨在通知AbstractAccountAuthenticator结果,但是我有一个没有它们的项目,它们有什么用?

问题2

onRequestContinued()为了什么?

问题 3

什么时候addAccount完成并创建帐户,应该在触发它时onActivityResult调用它?Activity

问题 4

如果在实现Intent中返回带有 key的 an,则将启动. 我注意到许多开发人员添加了附加功能。谁得到它们?AccountManager.KEY_INTENTaddAccountAbstractAccountAuthenticatorIntent

public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException
{
    Intent intent = new Intent(mContext, AuthenticatorActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType);     // <-- this
    intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);      // <-- this
    intent.putExtra(AuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);   // <-- this


    Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);

    return bundle;
}

回答

克里斯拉森

感谢你的回答。AccountManager老实说,我认为我们可能用错了。

我们想在我们的应用程序中共享一些凭据,所以我们有一个Service保存自定义帐户类型。由于应用程序知道帐户类型并共享签名证书,因此它们可以访问Account.

当每个应用程序启动时,它们都会尝试获取Account. 如果不存在,他们通过调用Account来触发我们的登录页面。ServiceAccountManager.addAccount(...)

一旦登录(通过 Web 服务)成功,我们就可以通过AccountManager.addAccountExplicitly(...). 在它之后不设定结果,不会影响结果。

它如何影响AccountManager?这种方法正确吗?

4

2 回答 2

3

编辑:我将描述如何——在经历了很多痛苦之后——我为我们的应用程序实现了身份验证服务。

你说你有一个Service验证器。我假设您以规定的方式做到了这一点:在您的清单中声明一个引用您的类的服务,该类是android.accounts.AbstractAccountAuthenticator. 清单条目还引用了一个 XML 文件,该文件带有account-authenticator声明您的帐户类型的标记,以及设置 | 使用的名称和图标。帐户页面。这些东西都有记录。

对于我们的应用程序,我希望最后登录的用户自动登录,直到他们在导航抽屉上选择“退出”。为此,将为SharedPreferences应用程序保留用户名。

因此,如果没有SharedPreferences用户名,应用程序会调用我们的自定义帐户类型,然后调用返回的意图。如果用户选择“添加新帐户”,则将在验证器上调用,如下所述。AccountManager.newChooseAccountIntent()startActivityForResultaddAccount()

我们的身份验证活动不仅有用于用户名/密码登录的 UI,还有密码重置和新试用帐户创建,因此有很多活动部分。当用户输入用户名/密码并按下登录按钮时,应用程序将针对我们的服务器进行身份验证。如果成功,应用程序会调用AccountManager.addAccountExplicitly()该用户名。

全部完成后,将AccountManager调用选择帐户活动。onActivityResult()如果我们得到RESULT_OK应用程序,则保留下一次登录操作的用户名。

如果SharedPreferences有一个用户名,并且客户经理有一个为其注册的帐户,我们不需要选择任何东西,直接跳到下一个阶段。

选择/已知帐户后,现在应用程序可以进行身份​​验证。它调用peekAuthToken()以查看令牌是否已注册,以及invalidateAuthToken()是否已注册。这样做是为了当应用程序调用getAuthToken()客户经理时,它会强制客户经理调用getAuthToken()应用程序的身份验证器以向服务器进行身份验证。由于服务器没有返回令牌,我们使用的是虚拟令牌,这就是它们每次都无效的原因。

现在关于这一切的有趣部分是,如果用户选择了一个已经注册的帐户,它将不会被验证,而如果他们选择添加新帐户并且该操作成功,则新帐户将被验证。所以请注意,如果您注意到登录新帐户会导致两次往返您的身份验证服务器,现在您知道原因了。(我setUserData()在帐户上使用了一些逻辑来表示已预先验证,但看起来我忘了完成该功能。嗯。)


让我们从一些背景开始澄清一些事情。

你有一些AbstractAccountAuthenticator. 您已创建此类以响应为其注册的帐户类型的身份验证请求。关于这个类要记住的是,平台AccountManager始终是您的应用程序将与之交互的组件;您的应用永远不会直接与此类进行交互。所以从某种意义上说,AccountManager它是您的身份验证服务的客户端。身份验证器执行的工作将在后台线程中进行。

现在,身份验证服务必须做的部分工作是与用户交互,询问用户帐户名、密码、指纹 ID、智能卡等。所以让我们假设您有一个AuthenticatorActivity可以完成这项工作的设备。通常,您将为此进行子类化android.accounts.AccountAuthenticatorActivity。这个活动课真的没什么特别的。它所做的唯一一件事就是在它开始时期望一个引用,然后在活动退出时AccountAuthenticatorResponse调用该响应的方法。onResult()

我使用的应用程序与您的应用程序相似,因为没有从身份验证服务返回令牌。我仍然实施getAuthToken有两个原因:

  • 与 Google 规定的工作流程保持一致。
  • 如果/当我们增强我们的身份验证服务以返回一个令牌时,可以很容易地返回一个真正的令牌。

因此,让我们跟随弹跳球来了解一切是如何组合在一起的。

  • 您的应用活动已实现该AccountManagerCallback接口,以便从AccountManager.
  • 假设您的应用程序已保留用户名以进行身份​​验证。您的应用使用此用户名创建一个Account对象。
  • 您的应用程序可以选择调用AccountManager.getAccountsByType()以确保帐户存在。
  • 您的应用程序调用AccountManager.getAuthToken()该帐户。
  • AccountManager看到您的身份验证器已为该帐户类型注册并调用身份验证器的方法getAuthToken
  • getAuthToken方法将联系您的身份验证服务器并获得响应。
  • 如果成功,您的身份验证器将返回(虚拟)身份验证令牌。

如果登录失败,那么事情就会变得有趣。

  • 由于您的身份验证器没有返回身份验证令牌,因此它必须返回启动您的意图,AuthenticatorActivity以便用户可以重新进行身份验证。

  • 使用AccountManager您的意图来启动您的AuthenticatorActivity. 作为请求的一部分,它将获得对AccountAuthenticatorResponse稍后需要的引用。

  • AuthenticatorActivity与用户交互,获取用户名/密码,联系您的服务器,获得响应。假设身份验证成功。
  • 您的活动将完成,并调用给定的onResult方法AccountAuthenticatorResponse
  • AccountManager响应对象获取通知时将调用带有结果的回调方法。

因此,我们可以回答您的问题:

方法setAccountAuthenticatorResult(Bundle)onResult(Bundle)旨在通知AbstractAccountAuthenticator结果,但是我有一个没有它们的项目,它们有什么用?

更准确地说,它们是为了通知AccountManager结果。你确定它有效吗?您是否尝试使用无效的用户名/密码登录? AccountManager除非另行通知,否则将假定身份验证已被取消。

onRequestContinued()为了什么?

我没有确切的答案。我的猜测是,它以AccountManager某种方式与 UI 在身份验证方面发生的事情保持同步。

如果在实现Intent中返回带有 key的 an,则将启动 Intent。我注意到许多开发人员添加了附加功能。谁得到它们?AccountManager.KEY_INTENTaddAccountAbstractAccountAuthenticator

答案是:你愿意。此意图将AuthenticatorActivity用于您为处理身份验证而创建的意图,因此您的活动执行身份验证所需的任何数据都需要与这些额外内容一起传递。

什么时候addAccount完成并创建帐户,应该在触发它时onActivityResult调用它?Activity

用例是当您的应用程序调用AccountManager.newChooseAccountIntent()然后调用startActivityForResult()结果意图时。然后AccountManager开始“选择帐户”活动。

现在,由于“添加新帐户”是“选择帐户” UI上的选项,因此选择该选项时AccountManager将调用您的身份验证者的addAccount()方法。然后,您的身份验证器将 aBundleAbstractAccountAuthenticator文档中所述返回到AccountManager. 在此之后,“选择帐户”活动将完成并调用onActivityResult()您的活动。

希望您开始看到AccountManager充当您的应用程序和身份验证器组件之间的代理。为了更清楚地说明这一点,请考虑您可以将这些组件打包成其他应用程序可以在不知道任何细节的情况下使用它们对您的服务进行身份验证的方式。如果您创建了自己的自定义帐户类型,则可以直接从设置 | 调用您的身份验证。设备的帐户页面。这是一个很好的测试,看看你的addAccount()方法是否正确实现。

于 2018-06-04T15:02:34.500 回答
2

方法 setAccountAuthenticatorResult(Bundle) 和 onResult(Bundle) 是为了通知 AbstractAccountAuthenticator 的结果,但是我有一个项目没有它们,它们有什么用?

这些方法(以及onError)是将结果传回给任何可能等待的人,例如看看AccountManager.getAuthToken(...)和许多其他让您指定AccountManagerCallback<Bundle>. 这是您的结果将被传递到的地方。null对回调有效,因此除非您围绕结果构建应用程序,否则它可以在没有它的情况下工作。

AccountAuthenticatorResponse.onResult(Bundle)如果您实现了从异步后端调用(例如令牌刷新)返回访问令牌的方式,则onError(...)更为重要。getAuthToken

onRequestContinued() 有什么用?

可用的信息很少(阅读:无)。我能找到的唯一一次使用它是在AccountManagerService它所在的位置,但是为了记录目的而增加的计数器。我认为它目前没有任何作用。

当 addAccount 完成并创建帐户时,是否应该在触发它的 Activity 上调用 onActivityResult?

只要你使用就应该工作startActivityForResult(..)

如果在 addAccount 实现中返回带有键 AccountManager.KEY_INTENT 的 Intent,AbstractAccountAuthenticator 将启动 Intent。我注意到许多开发人员添加了附加功能。谁得到它们?

正如 kris 已经提到的,您可以返回一个启动 Activity 的 Intent 并传递您可能需要的任何额外内容。


在不相关的说明中:您真的应该尽量不要以纯文本形式存储任何密码。即使 AccountManager 说它setPassword()不受保护,因此也不安全。

于 2018-06-09T15:28:31.953 回答