Androidlogin – 帐户validation器与手动validation

我即将在我的应用程序中实现与用户身份validation一起login

我的第一个想法是手动执行,向服务器注册用户名和密码,获取授权令牌,保存并在随后的请求中使用它。

在Googlesearch后,我收集到了在Android上正确使用帐户身份validation器的方法。 我已经看到了一些它的实现的例子,但我不明白这样做的好处吗? 是否因为我可以存储多个帐户? 是否因为同步问题? 如果有人能向我解释这一点,我将不胜感激。 这可能会让我更好地理解代码,为什么它正在做它。

Solutions Collecting From Web of "Androidlogin – 帐户validation器与手动validation"

我可以有多个帐户存储?

是。 看看谷歌Facebook如何做到这一点。

是否因为同步问题?

是的,您需要Account来使用SyncAdapter同步机制

为什么你应该使用AccountAuthenticator

  • 支持SyncAdapter等后台同步机制;

  • 用户authentication的标准方法;

  • 支持不同的令牌;

  • 帐户共享具有不同的权限

你需要做什么?

1)。 创buildAuthenticator ;

2)。 为用户login创buildActivity ;

3)。 创buildService与帐户通信。

条款。

AccountManager – 它pipe理设备上的帐户。 请求授权令牌,您应该使用AccountManager

AbstractAccountAuthenticator – 用于处理帐户types的组件。 它包含使用账户(授权,访问权限等)的所有逻辑。一个AbstractAccountAuthenticator可能被不同的应用程序使用(例如Gmail,Calendar,Drive等Google帐户)

AccountAuthenticatorActivity – 基础Activity ,用于授权/创build帐户。 如果需要识别帐户(帐户不存在或过期), AccountManager将调用此帐户。

它是如何工作的? 看下面的图片:

Android账户管理图

脚步。

1)。 创buildAuthenticator ;

您需要扩展AbstractAccountAuthenticator并覆盖7个方法:

  • Bundle editProperties(AccountAuthenticatorResponse response, String accountType) 链接
  • Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) 链接
  • Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) 链接
  • Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) 链接
  • String getAuthTokenLabel(String authTokenType) 链接
  • Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) 链接
  • Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) 链接

例:

 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; } } 

说明:

所以,你只需要看到两个方法: addAccountgetAuthToken

addAccount我添加了一些configuration参数,这些参数将被我的Activity for User Login使用。 这里的要点是intent.putExtra(Config.ARG_ACCOUNT_TYPE, accountType); – 你应该在这里指定账户types。 其他操作是没有必要的。

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" /> 

来自方法addAccountgetAuthToken摘要

尝试获取令牌,如果令牌存在返回结果,否则您将看到授权的Activity

2)。 为用户login创buildActivity ;

请参阅AuthenticatorActivity

简要说明:使用UserId和Password创build表单。 使用UserId&Password数据从服务器获取授权令牌,然后执行以下步骤:

 mAccountManager.addAccountExplicitly(account, accountPassword, null); mAccountManager.setAuthToken(account, authtokenType, authtoken); 

3)。 创build一个与帐户通信的Service

请参阅UdinicAuthenticatorService

不要忘了在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

对于源材料感谢

  • 乌迪·科恩

  • 丹尼尔Serdyukov (全文从他的文章翻译(除了我的小增加) “在Android应用程序中的同步。第1部分”仅在俄罗斯链接: http : //habrahabr.ru/company/e-Legion/blog/206210/ )

AccountManager有以下原因:

  • 首先是在一个帐户types下存储多个帐户名称,并具有不同级别的应用function访问权限。 例如,在videostream应用程序中,可能会有两个帐户名称:一个可以访问有限数量的video,另一个可以全天访问所有video。 这不是使用Accounts主要原因,但是,因为您可以轻松地在您的应用程序中pipe理它,而无需使用这个看起来Accounts
  • 使用Accounts的另一个好处是,每次用户请求授权function时,都要用用户名和密码去除传统的授权,因为authentication是在后台进行的,只有在一定的条件下才会要求用户input密码,我将在稍后讨论。
  • 在android中使用Accountsfunction也不需要定义自己的帐户types。 您可能会遇到使用Google帐户进行授权的应用程序,这可以节省制作新帐户和记住用户凭据的麻烦。
  • Accounts可以通过设置→账户独立添加
  • 跨平台的用户授权可以使用Accounts轻松pipe理。 例如,客户端可以在Android设备和PC中同时访问受保护的资料,而无需重复login。
  • 从安全的angular度来看,在服务器的每个请求中使用相同的密码可以在非安全连接中窃听。 密码encryption在这里是不够的,以防止密码被盗。
  • 最后,在Android中使用Accountsfunction的一个重要原因是将涉及任何依赖于Accounts业务(称为validation者和资源所有者)的双方分开,而不会影响客户端(用户)的凭据。 这些条款可能看起来相当模糊,但是在阅读下面的段落之前,不要放弃

让我用一个videostream应用程序的例子来说明后者。 A公司是与B公司签约的videostream媒体业务的持有者,为其某些会员提供优质的stream媒体服务。 公司B采用用户名和密码方法来识别用户。 对于A公司认可B的高级会员,一种方法是从B获得他们的名单,并使用类似的用户名/密码匹配机制。 这样,validation者和资源所有者是一样的(公司A)。 除了用户有义务记住第二个密码之外,他们很可能将他们的公司B的configuration文件设置为使用来自A的服务的相同密码。这显然是不利的。

为了消除上述缺点,引入了OAuth。 作为授权的开放标准,在上面的例子中,OAuth要求公司B(authentication者)通过为符合条件的用户(第三方)颁发一个称为访问令牌的令牌,然后向公司A(资源所有者)令牌。 所以没有标记意味着没有资格。

我在这里详细阐述了更多关于AccountManager更多信息

在Android的设置中,您有帐户types的帐户,从那里您可以添加一个帐户。 AccountManager也是存储凭证的中心位置,因此您只需为每个供应商login一次。 如果您多次下载其他谷歌应用程序或访问应用程序,则只能input一次凭据