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