Android中的OAuth实例状态

我正在尝试在Android应用中使用OAuth。 我有它正常工作,但有时在validation阶段遇到问题。 在Android中,我启动浏览器供用户login和validation。 然后callbackurl将redirect回我的应用程序。

这是问题。 我的应用程序有一个OAuth使用者和提供者作为我的主类的成员。 当浏览器启动进行身份validation时,有时我的主Activity被放弃以节省内存。 当callbackurl重新启动我的主要活动,供应商和消费者是新的实例,因此不工作时,我试图向api的请求。 如果主要Activiy在authentication阶段没有被释放,那么一切工作正常,因为我仍然与原始消费者和提供者一起工作。

我尝试使用onSaveInstanceState()和onRestoreInstanceState(),但没有成功。 看起来onRestoreInstanceState()在我的callbackurl被处理的时候没有被调用。 似乎直接去了onResume()。

在这种情况下坚持消费者和提供者的正确方法是什么?

Solutions Collecting From Web of "Android中的OAuth实例状态"

完成保存/恢复解决scheme

除了request_tokentoken_secretisOauth10a()状态在提供程序中很重要。 未来可能会有更多的国家信息。 因此,我喜欢坚持和负载解决scheme最好的。

我扩展了GrkEngineer的解决scheme,使其更加完整。 它将保存/恢复提供者和使用者,处理所有exception,并在恢复时设置httpClient。

 protected void loadProviderConsumer() { try { FileInputStream fin = this.openFileInput("tmp_provider.dat"); ObjectInputStream ois = new ObjectInputStream(fin); provider = (CommonsHttpOAuthProvider) ois.readObject(); provider.setHttpClient(httpClient); ois.close(); fin.close(); fin = this.openFileInput("tmp_consumer.dat"); ois = new ObjectInputStream(fin); consumer = (CommonsHttpOAuthConsumer) ois.readObject(); ois.close(); fin.close(); Log.d("OAuthTwitter", "Loaded state"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (StreamCorruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } protected void persistProviderConsumer() { try { FileOutputStream fout = this.openFileOutput("tmp_provider.dat", MODE_PRIVATE); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(provider); oos.close(); fout.close(); fout = this.openFileOutput("tmp_consumer.dat", MODE_PRIVATE); oos = new ObjectOutputStream(fout); oos.writeObject(consumer); oos.close(); fout.close(); Log.d("OAuthTwitter", "Saved state"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } 

我已经testing了这个代码,它的工作原理。

你可以在这里阅读我的旧post 。 通常我所做的是使用静态引用,并使用WebView而不是独立浏览器来显示authentication表单

我通过将提供者对象保存到文件来解决这个问题。 我正在使用路标库,提供者和使用者都是可序列化的。

 protected void loadProvider() { FileInputStream fin = this.openFileInput("provider.dat"); ObjectInputStream ois = new ObjectInputStream(fin); this.provider = (DefaultOAuthProvider) ois.readObject(); ois.close(); consumer = this.provider.getConsumer(); } protected void persistProvider() { FileOutputStream fout = this.openFileOutput("provider.dat", MODE_PRIVATE); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(this.provider); oos.close(); } 

在启动浏览器视图意图进行身份validation之前,我调用persist提供程序,并在调用provider.retrieveAccessToken()之前还原onResume()中的提供程序。 如果你在更多的位置调用persistProvider()和loadProvider(),你也可以让它保存适当的令牌后validation。 这将消除需要重新authentication(只要令牌有效)。

我仍然希望知道提供者类中的哪些字段实际上需要保留。 可能有点慢序列化整个对象。

可能原始海报有维护实例状态的问题的原因是因为Android的默认行为是为每个新的意图开始一个新的活动。 这就是为什么GrkEngineer在webcallback之后没有看到onRestoreInstanceState被调用的原因。

将您的请求令牌存储为共享首选项是一种解决scheme,以便可以从OAuth Webcallback之后启动的新活动中获取该请求令牌。

我最初尝试使用共享的喜好,它似乎工作正常。 但是,我不认为这是最好的解决scheme。 理想情况下,您想要强制Android将callback传递到您的原始活动(我将在下面解释原因)。

我尝试使用singleTask和singleInstance启动模式来完成这个部分成功,但它感觉错了,Android文档暗示这些模式不build议一般使用。

在深入挖掘文档和testing之后,我发现在创buildintent时使用下列标志,导致Android将意图传递给活动的现有实例(如果它已被杀死,则重新创build它)。

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

为什么我需要得到callback被原来的活动处理,所以我可以与Android AccountManager集成。 我使用下面的示例让我开始:

http://developer.android.com/resources/samples/SampleSyncAdapter/index.html

与AccountManager身份validation机制集成的关键部分之一是传递到您的活动中以启动身份validation过程的AccountAuthenticatorResponse。

我发现实现这个最大的问题是保持对AccountAuthenticatorResponse对象的引用。 这将传递到您的AuthenticatorActivity中,您需要在完成身份validation后调用其上的方法,以便标准帐户UI保持正确的状态。 但是,我遇到了GrkEngineer最初碰到的同样的问题。 当我尝试在OAuthcallback之后重新启动OAuth身份validation器活动时,我总是得到一个新的实例,该实例已经失去对AccountAuthenticatorResponse对象的引用,我看不到任何方法来保留该对象。

关键是要使用我上面描述的意图标志。

我的AbstractAccountAuthenticator使用FLAG_ACTIVITY_NEW_TASK启动AuthenticatorActivity。 它获取请求令牌(使用AsyncTask)并启动浏览器以要求用户授权。

OAuthCallbackHandlerActivity被注册来处理我的自定义callbackscheme。 在用户授予访问权限后被调用时,它会使用标志Intent.FLAG_ACTIVITY_NEW_TASK |调用AuthenticatorActivity。 Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP意图。

这会导致我的原始AuthenticatorActivity被重新激活。 AccountAuthenticatorResponse对象仍然可用(就像我在OnSaveInstanceState中保存的请求令牌/秘密一样)。 该活动现在可以获取访问令牌(再次使用AsyncTask),然后调用AccountAuthenticatorResponse对象上的完成方法。

使这个工作的关键是使用我提到的意图标志,并确保AuthenticatorActivity在应用程序任务中启动,而不是在帐户pipe理器任务中启动。 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP只会使一个活动的现有实例重用,如果它们在同一个任务中。 因此,如果您想要返回的活动是在其他任务中启动的,那么原始实例将不会被重用。

我使用开发工具在仿真器上testing了这一点,立即终止了AuthenticatorActivity,以便testing娱乐过程。 它使用onSaveInstanceState / onRestoreInstanceState来处理请求令牌/秘密。 而且我甚至不用担心恢复AccountAuthenticatorResponse对象。 这是由Android本身恢复 – 魔术!

你只需要持久化consumer.getToken()consumer.getTokenSecret()

稍后,您可以简单地重新创build一个新的consumer(customerKey,customerKeySecret)consumer.setTokenWithSecret(token, tokenSecret)

棘手的是要找出以下几点:

  1. 使用CommonsHttpOAuthConsumerCommonsHttpOAuthProvider (在android上) DefaultOAuthProvider将不起作用。

  2. 使用HttpURLConnection ,不能在消息负载中签署携带查询参数的POST请求

我有同样的问题。 所有你需要坚持的是在调用retrieveRequestToken之后得到的requestToken和tokenSecret。 在您的onResume()方法中,按照此处所述重新创build使用者和提供者对象。 这样你就不需要坚持整个消费者和提供者对象,并且仍然能够检索accessToken。