13

我目前正在使用 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:如果我缺少有关此问题的上下文、错误跟踪或其他信息,我深表歉意,但我是这里的新手。因此,如果您需要,请随时向我询问更多信息。

4

3 回答 3

6

一些背景故事/解释

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

因此,当您将其传递给请求以进行签名时,而不是从标头参数 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. 现在您需要更改类sign中的方法AbstractOAuthConsumer,其中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;
    }
    

    请注意,我只使用并尝试了第一个和最后一个。但这至少让你知道如何解决这个问题,如果你和我有同样的问题。

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

-德累斯顿先生

于 2013-03-12T15:46:00.183 回答
4

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

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

有关示例,请参见本文。

于 2014-04-07T09:00:48.230 回答
0

这个答案帮助了我。

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

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

于 2021-12-03T15:51:04.710 回答