对于一个应用来说,帐号管理真是一件麻烦事,所以通过OAuth之类的使用第三方帐号登录是常见的做法。关于这个实现,在Web端有很多现成的解决方案。我之前发过的《RESTful客户端库:RestClient 》也实现了一些(当时提供了饭否、foursquare和google,现在又增加了twitter)。
那么在移动端要怎么做?
Android系统内置了Google帐号(不要跟我谈被阉过的国行版,我们要放眼国际不是么,而且以此类推同样应该可以实现用别的帐号登录),如果能直接用这个登录岂不是方便?
当然,Google提供了这样的功能。
整个登录流程大体是这样:
* 应用在登录界面上显示当前手机中已经登录的所有Google帐号(可能不止一个)
* 用户在界面上选择一个帐号
* 程序向系统请求这个帐号验证(附带参数说明需要用这个帐号干什么,比如登录的话只需要取得用户的基本信息)
* Android在界面上弹出提示(显示什么应用请求什么帐号的什么权限)
* 用户选择同意
* Android向Google服务器提交请求
* Google服务器通过验证后返回一个token
* Android将token返回给应用程序
* 应用程序用这个Token登录应用服务端
* 应用服务端用这个token向Google服务器验证(调用取得用户基本信息的API)
* 成功登录
看上去很复杂,但大部分都是标准操作或是系统内置,对于用户来说很简单:
* 登录应用时选择一个帐号
* 确认
* 等待登录成功
根本不需要输入帐号密码什么的。
Android SDK里提供了AccountManager用于实现这一功能。
首先要取得当前手机里已登录的所有Google帐号:
private Account[] mAccounts; // ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAccounts = AccountManager.get(this.getActivity()).getAccountsByType("com.google"); for (Account a: mAccounts) { // a.name is account name } // ... }
说明:上面是以登录页面为一个fragment为例的,所以用了 this.getAcitivity() ,如果登录页面为一个activity,这里用this即可。"com.google"表示只取Google帐号,过滤掉其它帐号。
然后把取得的mAccounts列表显示到界面上,比如用一个ListView,这个视个人爱好,就不具体作代码举例了。
接下来,在用户点击一个帐号以后,需要依次启动两个异步任务:一个用于取得Google的token,一个用这个token登录应用后端。
在启动异步任务之前,在ListView的 onItemClick 事件里取得用户选择的帐号名,然后根据帐号名取得Account对象:
Account a = getAccountByName(name); AccountManagerFuture<bundle> amf = AccountManager.get( mActivity).getAuthToken(a, "oauth2:https://www.googleapis.com/auth/userinfo.email", null, mActivity, null, null);
说明:getAccountByName就是个循环,根据name从mAccounts里找到匹配的Account对象。mActivity为当前的Activity,如果是在Fragment里,记得用this.getActivity。
需 要注意的是其中的scope参数,就是例子中的这个"oauth2:https://www.googleapis.com/auth /userinfo.email"。表示用oauth2验证,权限为读取用户基本信息中的email地址信息,更多资料参见 Google API 文档(注意,不是Android开发文档)。
之后启动第一个异步任务:
@Override protected String doInBackground(Object... params) { try { return mFuture.getResult(30, TimeUnit.SECONDS).getString("authtoken"); } catch (OperationCanceledException e) { // ... } catch (AuthenticatorException e) { // ... } catch (IOException e) { // ... } return null; }
说明:AsyncTask的其它部分省略,其中mFuture就是上面取得的AccountManagerFuture<bundle>对象。返回的结果是一个Bundle,所以要调用getString方法取得authtoken。
因 为future.getResult方法是Android系统通过网络向Google服务器请求验证,其间Android还会向用户弹出提示请求的内容, 需要用户确认。所以这个方法的执行时间会比较长,如果不放到异步任务里执行的话,会把界面阻塞住,而且SDK对此也有限制(不放异步任务会报错)。这里只 是举例,所以设置了30秒的超时,实际应该设置得更长一些,给用户留点判断时间,甚至可以不带参数,无限时等待。
取得token以后的事情就是移动应用与应用后端之间的事情。通常就是一个REST请求把token提交给后端,由后端去验证(并取得用户登录身份)。
后端的实现可以用我之前那个RestClient库去调用Google API,取得用户的登录email,然后处理用户的登录操作。具体实现就是一般的第三方登录方式实现,这里略过不提。