使用 OAuth-Signpost 和 Apache HttpComponents 对 POST 请求进行签名的正确方法



我目前正在使用 OAuth-Signpost Java 库对从客户端发送到实现 OAuth 身份验证的服务器的请求进行签名。当发出GET请求(使用HttpURLConnection)时,一切正常:请求被签名,参数被包含,签名在目标中匹配。但是,它似乎不适用于 POST 请求。我知道在使用 HttpURLConnection 对 POST 进行签名时可能会出现的问题,所以我搬到了 Apache HttpComponents 库来处理这些请求。我在以下示例中发送的参数是纯字符串和类似 XML 的字符串 ('rxml')。我的代码如下:

public Response exampleMethod(String user, String sp, String ep, String rn, String rxml){
   //All these variables are proved to be correct (they work right in GET requests)
    String uri = "...";
    String consumerKey = "...";
    String consumerSecret = "...";
    String token = "...";
    String secret = "...";
  //create the parameters list
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    params.add(new BasicNameValuePair("user", user));
    params.add(new BasicNameValuePair("sp", sp));
    params.add(new BasicNameValuePair("ep", ep));
    params.add(new BasicNameValuePair("rn", rn));
    params.add(new BasicNameValuePair("rxml", rxml));
   // create a consumer object and configure it with the access
   // token and token secret obtained from the service provider
    OAuthConsumer consumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
    consumer.setTokenWithSecret(token, secret);
   // create an HTTP request to a protected resource
    HttpPost request = new HttpPost(uri);
   // sign the request      
    consumer.sign(request);       
   // set the parameters into the request
    request.setEntity(new UrlEncodedFormEntity(params));
   // send the request
    HttpClient httpClient = new DefaultHttpClient();
    HttpResponse response = httpClient.execute(request);
   //if request was unsuccessful
    if(response.getStatusLine().getStatusCode()!=200){
        return Response.status(response.getStatusLine().getStatusCode()).build();
    }
   //if successful, return the response body
    HttpEntity resEntity = response.getEntity();
    String responseBody = "";
    if (resEntity != null) {
        responseBody = EntityUtils.toString(resEntity);
    }
    EntityUtils.consume(resEntity);
    httpClient.getConnectionManager().shutdown();
    return Response.status(200).entity(responseBody).build();
}

当我向服务器发送 POST 请求时,我收到一个错误,告知签名(我发送的签名和服务器自己计算的签名)不匹配,所以我想这与他们正在签名的基本字符串和 POST 签名的工作方式有关,因为它们在两端处理相同的密钥和机密(已检查)。

我已经读到一种解决方法是将参数设置为URL的一部分(如在GET请求中)。不过这对我不起作用,因为 XML 参数可能会超过 URL 长度,因此需要将其作为 POST 参数发送。

我想我在签署 POST 请求或处理参数时做错了什么,但我不知道它是什么。拜托,你能帮帮我吗?

PS:如果我缺少有关此问题的上下文,错误跟踪或其他信息,我深表歉意,但我是这里的新手。因此,如果您需要,请随时向我询问更多信息。

一些背景故事/解释

在过去的几天里,我一直遇到类似的问题,几乎放弃了。直到我听说我公司的那个人正在提供与我通信的服务,已将它们配置为从查询字符串而不是标头参数中读取 OAuth 信息。

因此,当您将 Signpost 传递给

要签名时,Signpost 不会从 Signpost 放入请求中的标头参数 Authorization 中读取它,例如 [Authorization: OAuth oauth_consumer_key="USER", oauth_nonce="4027096421883800497", oauth_signature="Vd%2BJEb0KnUhEv1E1g3nf4Vl3SSM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1363100774", oauth_version="1.0"] ,尝试读取查询字符串的服务,例如http://myservice.mycompany.com?oauth_consumer_key=USER&oauth_nonce=4027096421883800497&oauth_signature=Vd%2BJEb0KnUhEv1E1g3nf4Vl3SSM%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1363100774&oauth_version=1.0 .

这样做的问题是,当我尝试对 url 进行签名,然后使用它构建 HttpPost 请求时,url 得到了一个前缀为 GET 而不是 POST 的基字符串,它给出了另一个签名,然后是服务计算的签名。Signpost 没有做错任何事,它的 url 签名方法只是默认设置为 GET,没有其他开箱即用的可能性。之所以如此,是因为您应该在执行 POST 时读取标头参数,而不是查询字符串(去挖我的那个"同事"的房子),并且 Signpost 在签署请求时添加这些参数,您在执行 POST 时应该这样做。

可以在 Signpost 中生成的 SigningBaseString 类方法中观察到 signingbasestring。

溶液

现在我就是这样做的,但其他方法可能是可能的,甚至更好。

  1. 获取路标源代码并将其添加到项目中。可以在这里得到它
  2. 找到 OAuthConsumer 类并更改签名方法,以便可以传递请求应为 POST 的信息。就我而言,我添加了一个布尔值,如下所示public String sign(String url, boolean POST)
  3. 现在,您需要更改AbstractOAuthConsumer类中的sign方法,该方法CommonsHttpOAuthConsumerDefaultOAuthConsumer扩展。就我而言,我将布尔变量添加到方法中,并在方法调用之前添加了以下if(POST) request.setMethod("POST"); sign(request);
  4. 现在请求是特定于 Signpost 的对象 HTTPRequest,因此这将引发错误。您需要更改它并添加方法public void setMethod(String method); .
  5. 这将在以下类中导致错误 HttpURLConnectionRequestAdapterHttpRequestAdapterUrlStringRequestAdapter 。您需要将方法实现添加到所有这些方法中,但风格不同。对于第一个,您将添加

    public void setMethod(String method){
    try {
     this.connection.setRequestMethod(method);
     } catch (ProtocolException e) {
        e.printStackTrace();
     }
    }
    

    对于第二个,您将添加

    public void setMethod(String method){
       try {
           RequestWrapper wrapper = new RequestWrapper(this.request);
           wrapper.setMethod(method);
           request = wrapper;
       } catch (org.apache.http.ProtocolException e) {
        e.printStackTrace();
        }   
    }
    

    最后,您将添加

    public void setMethod(String method){
       mMethod = method;
    }
    

    请注意,我只使用并尝试了第一个和最后一个。但这至少可以让您了解如何解决问题,如果您与我有相同的问题。

希望这无论如何都有帮助。

-德累斯顿先生

使用 oauth-signpostHttpURLConnection 对 POST 请求进行签名是可行的,但它需要一些技巧:

诀窍是对 POST 参数进行百分比编码,并使用方法 setAdditionalParameters() 将它们添加到 OAuth 库中。

有关示例,请参阅本文。

这个答案帮助了我。

但是对于PALINTEXT方法,您不需要参数和url.
他们不会更改签名。签名是恒定的,并且基于机密。

但是对于 SHA1(和其他方法),您可以使用上述答案。

最新更新