我即将在我的应用中实施登录和用户身份验证。
我的第一个想法是手动完成,向服务器注册用户名和密码,获取身份验证令牌,保存并在后续请求中使用它。
在谷歌搜索后,我收集到了在Android上正确使用帐户身份验证器的方法。我已经看到了一些实施例子,但我不明白这样做的好处吗?是因为我可以存储多个帐户吗?是因为同步问题吗?如果有人能向我解释,我会很感激。它可能会让我更好地理解它的代码以及它为什么会这样做。
答案 0 :(得分:97)
我可以存储多个帐户吗?
是。了解 Google 或 Facebook 如何做到这一点。
是因为同步问题吗?
是的,您需要帐户才能使用SyncAdapter
为什么要使用AccountAuthenticator
?
支持SyncAdapter
;
验证用户身份的标准方法;
支持不同的令牌;
具有不同权限的帐户分享
你需要做什么?
1)。创建Authenticator
;
2)。为用户登录创建Activity
;
3)。创建Service
以与帐户进行通信。
AccountManager - 它管理设备上的帐户。请求使用AccountManager
。
AbstractAccountAuthenticator - 用于处理帐户类型的组件。它包含处理帐户的所有逻辑(授权,访问权限等)。不同的应用程序可能会使用一个AbstractAccountAuthenticator
(例如Gmail帐户,日历,云端硬盘等)。
AccountAuthenticatorActivity - 基础Activity
,用于授权/创建帐户。 AccountManager
如果需要识别帐户(令牌不存在或已过期),则会调用此帐户
它是如何工作的?看下面的图片:
1)。创建Authenticator
;
您需要扩展AbstractAccountAuthenticator
并覆盖7种方法:
Bundle editProperties(AccountAuthenticatorResponse response, String
accountType)
link Bundle addAccount(AccountAuthenticatorResponse response, String
accountType, String authTokenType, String[] requiredFeatures, Bundle
options)
link Bundle confirmCredentials(AccountAuthenticatorResponse response,
Account account, Bundle options)
link Bundle getAuthToken(AccountAuthenticatorResponse response, Account
account, String authTokenType, Bundle options)
link String getAuthTokenLabel(String authTokenType)
link Bundle updateCredentials(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle options)
link Bundle hasFeatures(AccountAuthenticatorResponse response, Account
account, String[] features)
link 示例:
public class LodossAuthenticator extends AbstractAccountAuthenticator {
private static final String LOG_TAG = LodossAuthenticator.class.getSimpleName();
private final Context mContext;
public LodossAuthenticator(Context context) {
super(context);
mContext = context;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
final Intent intent = new Intent(mContext, CustomServerAuthenticatorSigninActivity.class);
intent.putExtra(Config.ARG_ACCOUNT_TYPE, accountType);
intent.putExtra(Config.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(Config.ARG_IS_ADDING_NEW_ACCOUNT, true);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
// If the caller requested an authToken type we don't support, then
// return an error
if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
return result;
}
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
// Lets give another try to authenticate the user
if (TextUtils.isEmpty(authToken)) {
final String password = am.getPassword(account);
if (password != null) {
try {
authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// If we get an authToken - we return it
if (!TextUtils.isEmpty(authToken)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
return result;
}
// If we get here, then we couldn't access the user's password - so we
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity.
final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(com.lodoss.authlib.Config.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(com.lodoss.authlib.Config.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(Config.ARG_ACCOUNT_NAME, account.name);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
if (AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
return AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
else if (AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
return AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY_LABEL;
else
return authTokenType + " (Label)";
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putBoolean(KEY_BOOLEAN_RESULT, false);
return result;
}
}
说明:
因此,您只需要查看两种方法:addAccount
,getAuthToken
。
在addAccount
我添加了一些配置参数,我的Activity
将用于用户登录。这里的要点是intent.putExtra(Config.ARG_ACCOUNT_TYPE, accountType);
- 您应该在此处指定帐户类型。其他操作不是必需的。
在getAuthToken
- 请阅读评论。我已从UdinicAuthenticator.java
此外,您需要在AndroidManifest.xml中使用以下权限:
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
addAccount
和getAuthToken
尝试获取令牌,如果令牌存在,则返回结果,否则您将看到Activity
进行授权
2)。为用户登录创建Activity
;
简要说明: 使用UserId和密码创建表单。使用UserId&amp;密码数据从服务器获取身份验证令牌,然后执行以下步骤:
mAccountManager.addAccountExplicitly(account, accountPassword, null);
mAccountManager.setAuthToken(account, authtokenType, authtoken);
3)。创建Service
以与帐户进行通信。
不要忘记将此行添加到AndroidManifest.xml
到Service
:
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
还在res/xml
添加文件authenticator.xml
:
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.mediamanagment.app"
android:icon="@drawable/ic_launcher"
android:smallIcon="@drawable/ic_launcher"
android:label="@string/authenticator_label"/>
这就是全部。您可以使用AccountAuthenticator
。
Daniel Serdyukov(所有文字均来自他的文章(除了我的文章) 小增加)“Android应用程序中的同步。第1部分” 仅限俄语链接: http://habrahabr.ru/company/e-Legion/blog/206210/)
答案 1 :(得分:7)
AccountManager
因以下原因而有用:
Accounts
的主要原因,但是,因为您可以在应用中轻松管理它,而无需使用这种看似奇特的Accounts
... Accounts
的另一个好处是,每次用户请求授权功能时,都会使用用户名和密码取消传统授权,因为身份验证是在后台进行的,并且要求用户他们的密码仅在某些条件下,我稍后会知道。Accounts
功能也无需定义自己的帐户类型。您可能会遇到使用Google帐户进行授权的应用,这样可以省去制作新帐户和记住用户凭据的麻烦。Accounts
可以通过设置→帐户Accounts
轻松管理跨平台用户授权。例如,客户端可以在他们的Android设备和PC中同时访问受保护的材料,而无需重复登录。Accounts
功能的一个重要原因是将依赖于Accounts
的所有业务中涉及的双方分开,即所谓的身份验证者和资源所有者,而不会损害客户端(用户) )的证书。这些条款似乎相当含糊,但在阅读以下段落之前不要放弃...... 让我详细说明后者的视频流应用程序示例。 A公司是与B公司签订合同的视频流业务的持有者,为其某些成员提供优质流媒体服务。公司B使用用户名和密码方法来识别其用户。对于公司A来认可B的高级会员,一种方法是从B获取它们的列表并使用类似的用户名/密码匹配机制。这样,验证者和资源所有者是相同的(公司A)。除了用户有义务记住第二个密码之外,很可能他们设置了与公司B的个人资料相同的密码来使用来自A的服务。这显然是不利的。
为了消除上述缺点,引入了OAuth。作为授权的开放标准,在上面的示例中,OAuth要求授权由公司B(身份验证者)通过为符合条件的用户(第三方)发布一些称为访问令牌的令牌,然后向公司A(资源所有者)提供令牌。所以没有令牌意味着没有资格。
我在here
的网站AccountManager
上详细阐述了这一点及更多内容
答案 2 :(得分:0)
在Android的设置中,您拥有帐户类型的帐户,然后您可以添加帐户。 AccountManager也是存储凭证的中心位置,因此您只需为每个供应商登录一次。如果您下载其他Google应用或多次访问应用,则只需输入一次凭据