代码之家  ›  专栏  ›  技术社区  ›  GrkEngineer

Android中的OAuth实例状态

  •  9
  • GrkEngineer  · 技术社区  · 15 年前

    我正在尝试在Android应用程序中使用OAuth。我让它正常工作,但在身份验证阶段有时会遇到问题。在Android中,我启动浏览器,让用户登录和验证。然后回调URL将重定向回我的应用程序。

    这就是问题所在。我的应用程序有一个OAuth使用者和提供者作为我的主类的成员。启动浏览器进行身份验证时,有时会丢弃我的主要活动以保存内存。当回调URL重新启动我的主要活动时,提供者和使用者是新的实例,因此当我尝试向API发出请求时,它们不起作用。如果在身份验证阶段没有释放主活动,那么一切都正常工作,因为我仍在与原始的使用者和提供者一起工作。

    我尝试使用OnSaveInstanceState()和OnRestoreInstanceState(),但没有成功。处理回调URL时,似乎未调用OnRestoreStanceState()。似乎直接转到onresume()。

    在这种情况下,保持使用者和提供者的正确方法是什么?

    6 回复  |  直到 13 年前
        1
  •  3
  •   HRJ    14 年前

    完整的保存/还原解决方案

    除了 request_token token_secret , the isOauth10a() 状态对于在提供程序中恢复很重要。将来可能会有更多的状态信息。因此,我最喜欢持久性和负载解决方案。

    我延长了 软件工程师 更完整的解决方案。它保存/还原提供者和使用者,处理所有异常,并在还原时设置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();
      }
    }
    

    我已经测试过这个代码,它可以工作。

        2
  •  3
  •   Bostone    15 年前

    你可以读懂我的 old post here . 一般来说,我所做的是使用静态引用,并将活动与WebView一起使用,而不是使用独立的浏览器来显示身份验证表单。

        3
  •  3
  •   GrkEngineer    15 年前

    我通过将提供程序对象持久化到文件来解决这个问题。我使用的是SignPost库,提供者和使用者都是可序列化的。

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

    我在启动浏览器视图以进行身份验证之前调用了Persistent提供程序,并在调用provider.retrieveAccessToken()之前在onResume()中还原该提供程序。如果在多个位置调用PersistProvider()和LoadProvider(),也可以让它保存适当的令牌进行身份验证。这将消除重新认证的需要(只要令牌有效)。

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

        4
  •  2
  •   Martin Bayly    14 年前

    原始海报在维护实例状态时出现问题的原因可能是Android的默认行为是为每个新的意图启动一个新的活动。这就是为什么grkenginer没有看到OnRestoreStanceState在Web回调之后被调用的原因。

    将请求令牌存储为共享首选项是一种解决方案,这样就可以从OAuth Web回调后启动的新活动访问它。

    我最初尝试使用共享的首选项,但效果似乎不错。但是,我不认为这是最好的解决办法。理想情况下,您希望强制Android将回调传递给您的原始活动(我将在下面解释原因)。

    我尝试使用单任务和单实例启动模式来实现这一点,但有一点是错误的,android文档提示这些模式不推荐用于一般用途。

    经过大量的文档挖掘和测试,我发现在创建意图时使用以下标志会导致Android将意图传递到活动的现有实例(如果它被杀死,则重新创建它)。

    intent.setflags(intent.flag_activity_new_task_intent.flag_activity_clear_top_intent.flag_activity_single_top);

    我之所以需要让回叫由原始活动处理,是因为我可以与Android AccountManager集成。我使用以下示例开始:

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

    与accountmanager authenticator机制集成的关键部分之一是传递到活动中以启动身份验证过程的accountauthenticatorResponse。

    我发现实现这一点的最大问题是保持对accountauthenticatorresponse对象的引用。这会传递到authenticatorActivity中,您需要在完成身份验证后调用它的方法,以便标准帐户UI保持正确的状态。但是,我遇到了和Grkenginer最初遇到的问题相同的问题。当我试图在OAuth回调后重新启动我的OAuth身份验证程序活动时,我总是得到一个新实例,它丢失了对accountauthenticatorresponse对象的引用,我看不到任何方法来保持该对象。

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

    authenticatorActivity是由我的abstractAccountauthenticator使用标记_activity_new_任务启动的。它获取请求令牌(使用AsyncTask),并启动浏览器请求用户进行授权。

    OAuthCallbackHandlerActivity已注册以处理我的自定义回调方案。当用户授予访问权限后被调用时,它使用标记intent.flag_activity_new_task_intent.flag_activity_clear_top_intent.flag_activity_single_top intent调用authenticatorActivity。

    这将导致重新激活我的原始authenticatoractivity。accountauthenticatorresponse对象仍然可用(与我保存在OnSaveInstanceState中的请求令牌/机密相同)。现在,该活动可以获取访问令牌(再次使用AsyncTask),然后调用accountAuthenticatorResponse对象上的完成方法。

    实现这一点的关键是使用我提到的意图标志,并确保authenticatorActivity是在应用程序任务而不是帐户管理器任务中启动的。flag_activity_new_task_flag_activity_single_top仅会导致活动的现有实例在同一任务中被重用。因此,如果要返回的活动是在其他任务中启动的,那么原始实例将不会被重新使用。

    我在一个模拟器上测试了这个,使用dev工具立即终止我的authenticatoractivity,这样我就可以测试重新创建过程。它使用onsaveInstanceState/onRestoreInstanceState为请求令牌/秘密工作得很好。我甚至不用担心恢复accountauthenticatorresponse对象。那是安卓自己修复的-魔法!

        5
  •  2
  •   takrl cck    13 年前

    你只需要坚持下去 consumer.getToken() consumer.getTokenSecret()

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

    很难弄清楚的是:

    1. 使用 CommonsHttpOAuthConsumer CommonsHttpOAuthProvider (在Android上)The DefaultOAuthProvider 不会起作用。

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

        6
  •  1
  •   Community CDub    7 年前

    我也有同样的问题。您只需要保留在调用retrieverequesttoken之后得到的requesttoken和tokensecret。 在onresume()方法中,按照说明重新创建使用者和提供者对象 here . 这样,您就不需要持久化整个使用者和提供者对象,并且仍然能够检索accesstoken。