Подтвердить что ты не робот

Каков правильный способ подписывать POST-запросы с помощью OAuth-Signpost и Apache HttpComponents?

В настоящее время я использую библиотеку Java OAuth-Signpost для подписывания запросов, отправленных с клиента на сервер, который реализует аутентификацию OAuth. При создании запросов GET (с использованием HttpURLConnection) все работает нормально: запросы подписаны, параметры включены, а подписи совпадают по назначению. Однако, похоже, он не работает с запросами POST. Я знаю о проблемах, которые могут возникнуть при подписании POST с использованием HttpURLConnection, поэтому я перешел в библиотеку 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: Я извиняюсь, если мне не хватает контекста, ошибок ошибок или дополнительной информации по этой проблеме, но я новичок здесь. Поэтому, пожалуйста, не стесняйтесь спрашивать меня, если вам это нужно.

4b9b3361

Ответ 1

Немного предыстории/объяснения

У меня была аналогичная проблема за последние пару дней, и я почти сдался. До тех пор, пока я не услышал, что парень из моей компании, который размещал службы, с которыми я общался, настроил их для чтения информации OAuth из строки запроса вместо параметров заголовка.

Поэтому вместо того, чтобы читать его из параметра заголовка "Авторизация", который 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&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.

Функция signbasestring может быть обнаружена в методе класса SigningBaseString в Signpost.

Решение

Теперь вот как я это сделал, но другие способы могут быть возможны или даже лучше.

  • Получить исходный код указателя и добавить его в свой проект. Можно получить здесь
  • Найдите класс OAuthConsumer и измените метод подписи, чтобы вы могли передавать информацию о том, что запрос должен быть POST. В моем случае я добавил логическое значение типа public String sign(String url, boolean POST)
  • Теперь вам нужно изменить метод sign в классе AbstractOAuthConsumer, который CommonsHttpOAuthConsumer и DefaultOAuthConsumer расширяются. В моем случае я добавил логическую переменную в метод и следующий if(POST) request.setMethod("POST"); прямо перед вызовом метода sign(request);
  • Теперь запрос является специфическим объектом Signpost, HTTPRequest, поэтому это вызовет ошибку. Вам нужно будет изменить его и добавить метод public void setMethod(String method);.
  • Это приведет к ошибке в следующих классах HttpURLConnectionRequestAdapter, HttpRequestAdapter и UrlStringRequestAdapter. Вам нужно будет добавить реализацию метода для всех, но в разных вариантах. Для первого вы добавите

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

    Будьте предупреждены, что я использовал и пробовал только первый и последний. Но это по крайней мере дает вам представление о том, как исправить проблему, если у вас есть тот же, что и я.

Надеюсь, это поможет в любом случае.

-MrDresden

Ответ 2

Подписание POST-запросов с использованием oauth-signpost и HttpURLConnection выполнимо, но требует небольшого взлома:

Хитрость заключается в процентном кодировании параметров POST и добавлении их в библиотеку OAuth с помощью метода setAdditionalParameters().

Смотрите эту статью для примера.