使用 OAuth-Signpost 和 Apache HttpComponents 签署 POST 请求的正确方法是啥?

Posted

技术标签:

【中文标题】使用 OAuth-Signpost 和 Apache HttpComponents 签署 POST 请求的正确方法是啥?【英文标题】:What is the right way to sign POST requests with OAuth-Signpost and Apache HttpComponents?使用 OAuth-Signpost 和 Apache HttpComponents 签署 POST 请求的正确方法是什么? 【发布时间】:2013-01-14 02:33:33 【问题描述】:

我目前正在使用 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 请求或处理参数时做错了,但我不知道它是什么。拜托,你能帮帮我吗?

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

【问题讨论】:

我自己实际上也遇到了这个问题。如果你发现它是什么,你能分享你的发现吗?如果我发现是什么原因,我也会这样做。 嗨,这里的 sp、ep、rn 和 rxml 是什么?提前致谢 【参考方案1】:

这个answer 帮助了我。

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

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

【讨论】:

【参考方案2】:

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

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

有关示例,请参阅 this article。

【讨论】:

【参考方案3】:

一些背景故事/解释

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

因此,不是从标头参数 Authorization 中读取它,而是在您将 Signpost 传递给请求以进行签名时放入请求中,例如 [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&amp;oauth_nonce=4027096421883800497&amp;oauth_signature=Vd%2BJEb0KnUhEv1E1g3nf4Vl3SSM%3D&amp;oauth_signature_method=HMAC-SHA1&amp;oauth_timestamp=1363100774&amp;oauth_version=1.0

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

SigningBaseString 可以在 Signpost 中的 SigningBaseString 类方法 generate 中观察到。

解决方案

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

    获取路标源代码并将其添加到您的项目中。可以得到它here 找到OAuthConsumer 类并更改签名方法,以便您可以传递请求应该是POST 的信息。在我的例子中,我添加了一个布尔值,像 public String sign(String url, boolean POST) 现在您需要更改AbstractOAuthConsumer 类中的sign 方法,该方法是CommonsHttpOAuthConsumerDefaultOAuthConsumer 扩展的。在我的情况下,我在方法调用 sign(request); 之前将布尔变量添加到方法和以下 if(POST) request.setMethod("POST"); 现在请求是一个特定于路标的对象,HTTPRequest,所以这将引发错误。您需要更改它并添加方法public void setMethod(String method);

    这将导致以下类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;
    
    

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

希望这对您有所帮助。

-德累斯顿先生

【讨论】:

效果很好。 AbstractOAuthConsumer 类首先给出抽象方法我生成 jar 抽象方法没有覆盖错误,删除了签名方法代码,然后再次覆盖并工作。

以上是关于使用 OAuth-Signpost 和 Apache HttpComponents 签署 POST 请求的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

mac 打开apach 但无法访问localhost的解决方法

Apach Shiro 密码加密过程(明文生成密码过程)详细解析

apach+svn

漏洞风险提示|Apach Solr JMX配置默认开启导致远程命令执行漏洞

mysql apach php

idea中导包出现import org.apach.*,提交代码老出现冲突,不想使用.*的设置