Write your own Android Authenticator (帐号基本原理)

Write your own Android Authenticator
http://blog.udinic.com/2013/04/24/write-your-own-android-authenticator/

一、Android的账号体系基本原理

  1. AccountManager用于管理各个类型的账号,可以通过它获取和添加账号信息

  2. AccountAuthenticator一个用于处理特定类型账号的模块。AccountManager通过找到相应的类型的AccountAuthenticator
    去进行该类型账号的一系列操作。AccountAuthenticator知道展示哪个Activity给用户进行认证登录,并知道从哪里找到之前存储的服务器返回的auth-token给用户。

  3. AccountAuthenticatorActivity是登录和创建账号Activity的基类。当用户需要登录认证的时候被AccountAuthenticator调用展示给用户。该Activity负责处理和服务器进行的登录和创建账号过程,并在成功后将auth-token返回给调用的AccountAuthenticator。

  4. 为了让AccountAuthenticator可以被所有APP使用,同时也无需界面交互,我们需要创建Service。让其他进程绑定到我们的Service并和AccountAuthenticator进行交流。

    —— ------------- ------------- ——

  5. Authentication Token (auth-token) :服务器返回的临时访问token。用户通过登录认证获得它,并在和服务器以登录态进行交互时需要带着该token。

  6. 认证服务器,该服务器管理着所有的用户,它将会在用户登录的时候产生一个auth-token给用户;并在用户后续的登录态交互中对其(auth-token)进行验证。auth-token可以设置有效时间,当过了有效时间后便失效了。

二、调用和内部创建流程

  1. 外部调用
AccountManager.get(this).addAccount(MY_ACCOUNT_TYPE, null, null, null, null, new AccountManagerCallback<Bundle>() {
	@Override
	public void run(AccountManagerFuture<Bundle> future) {
		try {
			Bundle bundle = future.getResult();
			if (bundle != null && bundle.containsKey(AccountManager.KEY_INTENT)) {
				Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
				startActivity(intent);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
}, null);
  1. 内部创建Authenticator
public class MyAuthenticator extends  AbstractAccountAuthenticator {
	
	// 当用户登录和创建一个新的账号的时候调用
	@Overide
	public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
   	 	final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
     	intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType);
    	intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
    	intent.putExtra(AuthenticatorActivity.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;
	}

	// 获取该类型账号之前登录成功而存储的auth-token
	@Override
	public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetWordErrorException {
		// Extract the username and password from the Accounot Manager,
		// and ask the server for an appropriate AuthToken.
		final AccountManager am = AccountManager.get(mContext);
		String authToken = am.peekAuthToken(account, authTokenType);

		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 auth-token
		// 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_RESPOONSE, response);
		intent.putExtra(AuthticatorActivity.ARG_ACCOUNT_TYPE, account.type);
		intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
		final Bundle bundle = new Bundle();
		bundle.putParcelable(AccountManager.KEY_INTENT, intent);
		return bundle;
	}
		


}
  1. 创建登录注册Activity
// The reason we extend from AccountAuthenticatorActivity is 
// the setAccountAuthenticatorResult()method. this method is 
// in charge of taking back the result from the authentication process
// on the activity and return it to the Authticator, who called this activity
// in the first place.
public class LoginActivity extends AccountAuthenticatorActivity {

	...
	// when submitting I call this method
	public void submit() {
		final String userName = ((TextView)findViewById(R.id.name)).getText().toString();
		final String userPass = ((TextView)findViewById(R.id.pass)).getText().toString();

		new AsyncTask<Void, Void, Intent>() {
			@Override
			protected Intent doInBackground(Void...params) {

				// sServerAuthenticate is the interface to our authenticating server.
				// mAuthTokenType is the type ot token that I request form the server. I can have the server give me different token for read-only or full access to an account, or even for different sevices within the same account. A good example is the Google account, which provides several auth-token types:"_Manage your calendars", "Manage your _tasks", "View your calendars" and more.. 
				String authToken = sServerAuthenticate.userSignIn(userName, userPass, mAuthTokenType);
				final Intent res = new Intent();
				res.putExtra(AccountManager.KEY_ACCOUNT_NAME, userName);
				res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, ACOUNT_TYPE);
				res.putExtra(AccountManager.KEY_AUTHTOKEN, authToken);
				return res;
			}

			@Override
			protected void onPostExecute(Intent intent) {
				finishLogin(intent);
			}
		}.execute();
	}
	
	private void finishLogin(Intent intent) {
		String accountName = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
		final Account account = new Account(accountName, intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE));
		if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) {
			String authToken = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN);
			String authTokenType = mAuthTokenType;
			
			// Creating the account on the device and setting the auth token we got
			// if not setting the auth token will cause another call to the server to authenticate the user
			mAccountManager.setAuthToken(account, authTokenType, authToken);
			
		}
		setAccountAuthenticatorResult(intent.getExtras());
		setResult(RESULT_OK, intent);
		finish();
		
	}
}
  1. 创建Service
public class MyAuthenticatorService extends Service {
	@Overrice
	public IBinder onBind(Intent intent) {
		MyAuthenticator authenticator = new MyAuthenticator(this);
		return authenticator.getIBinder();
	}
}

and on the manifest we need to add our service with the

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

The authenticator.xml that we link as a resource, is
used to define some attributes for our Authenticator.
That’s how it looks:

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
                       android:accountType="com.udinic.auth_example"
                       android:icon="@drawable/ic_udinic"
                       android:smallIcon="@drawable/ic_udinic"
                       android:label="@string/label"
                       android:accountPreferences="@xml/prefs"/>

其中accountType 是唯一的名称用于区分和表明你的账号类型。 任何时候App想要使用我们认证,它需要知道accountType。

其中icon 和 smallIcon是 图标被展示在设备设置页面和账号批准页面

lable 是一个代表你账号的字符串,被列在设备设置页面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值