编辑:我将描述如何——在经历了很多痛苦之后——我为我们的应用程序实现了身份验证服务。
你说你有一个Service
验证器。我假设您以规定的方式做到了这一点:在您的清单中声明一个引用您的类的服务,该类是android.accounts.AbstractAccountAuthenticator
. 清单条目还引用了一个 XML 文件,该文件带有account-authenticator
声明您的帐户类型的标记,以及设置 | 使用的名称和图标。帐户页面。这些东西都有记录。
对于我们的应用程序,我希望最后登录的用户自动登录,直到他们在导航抽屉上选择“退出”。为此,将为SharedPreferences
应用程序保留用户名。
因此,如果没有SharedPreferences
用户名,应用程序会调用我们的自定义帐户类型,然后调用返回的意图。如果用户选择“添加新帐户”,则将在验证器上调用,如下所述。AccountManager.newChooseAccountIntent()
startActivityForResult
addAccount()
我们的身份验证活动不仅有用于用户名/密码登录的 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
方法将联系您的身份验证服务器并获得响应。
- 如果成功,您的身份验证器将返回(虚拟)身份验证令牌。
如果登录失败,那么事情就会变得有趣。
因此,我们可以回答您的问题:
方法setAccountAuthenticatorResult(Bundle)
和onResult(Bundle)
旨在通知AbstractAccountAuthenticator
结果,但是我有一个没有它们的项目,它们有什么用?
更准确地说,它们是为了通知AccountManager
结果。你确定它有效吗?您是否尝试使用无效的用户名/密码登录? AccountManager
除非另行通知,否则将假定身份验证已被取消。
是onRequestContinued()
为了什么?
我没有确切的答案。我的猜测是,它以AccountManager
某种方式与 UI 在身份验证方面发生的事情保持同步。
如果在实现Intent
中返回带有 key的 an,则将启动 Intent。我注意到许多开发人员添加了附加功能。谁得到它们?AccountManager.KEY_INTENT
addAccount
AbstractAccountAuthenticator
答案是:你愿意。此意图将AuthenticatorActivity
用于您为处理身份验证而创建的意图,因此您的活动执行身份验证所需的任何数据都需要与这些额外内容一起传递。
什么时候addAccount
完成并创建帐户,应该在触发它时onActivityResult
调用它?Activity
用例是当您的应用程序调用AccountManager.newChooseAccountIntent()
然后调用startActivityForResult()
结果意图时。然后AccountManager
开始“选择帐户”活动。
现在,由于“添加新帐户”是“选择帐户” UI上的选项,因此选择该选项时AccountManager
将调用您的身份验证者的addAccount()
方法。然后,您的身份验证器将 aBundle
如AbstractAccountAuthenticator
文档中所述返回到AccountManager
. 在此之后,“选择帐户”活动将完成并调用onActivityResult()
您的活动。
希望您开始看到AccountManager
充当您的应用程序和身份验证器组件之间的代理。为了更清楚地说明这一点,请考虑您可以将这些组件打包成其他应用程序可以在不知道任何细节的情况下使用它们对您的服务进行身份验证的方式。如果您创建了自己的自定义帐户类型,则可以直接从设置 | 调用您的身份验证。设备的帐户页面。这是一个很好的测试,看看你的addAccount()
方法是否正确实现。