Как правильно подписать GET-запрос к Amazon ItemLookup, используя только клиентский JavaScript?

Вот что у меня есть до сих пор:

function sha256(stringToSign, secretKey) {
  return CryptoJS.HmacSHA256(stringToSign, secretKey);
} 

function getAmazonItemInfo(barcode) {

  var parameters = 
    "Service=AWSECommerceService&"
    + "AWSAccessKeyId=" + appSettings.amazon.accessKey + "&"
    + "Operation=ItemLookup&"
    + "ItemId=" + barcode
    + "&Timestamp=" + Date.now().toString();

  var stringToSign =
    "GET\n"
    + "webservices.amazon.com\n"
    + "/onca/xml\n"
    + parameters;

  var signature = "&Signature=" + encodeURIComponent(sha256(stringToSign, appSettings.amazon.secretKey));

  var amazonUrl =  
    "http://webservices.amazon.com/onca/xml?"
    + parameters
    + signature;

  // perform a GET request with amazonUrl and do other stuff

}

При выполнении в виде HTTP-запроса GET значение amazonUrl в приведенном выше коде приводит к следующему ответу от Amazon:

<?xml version="1.0"?> 
  <ItemLookupErrorResponse xmlns="http://ecs.amazonaws.com/doc/2005-10-05/">
    <Error>
      <Code>SignatureDoesNotMatch</Code>
      <Message>
        The request signature we calculated does not match the signature you provided. 
        Check your AWS Secret Access Key and signing method. Consult the service 
        documentation for details.
      </Message>
   </Error>
   <RequestId>[REMOVED]</RequestId>
  </ItemLookupErrorResponse>

Полезные ссылки:

ItemLookup — документация Amazon по API рекламы продуктов

Примеры запросов REST

Процесс аутентификации AWS

КриптоJS


person Community    schedule 23.03.2014    source источник
comment
Это также может помочь: github.com/livelycode/aws -lib/blob/master/examples/prod-adv.js   -  person    schedule 23.03.2014
comment
Я полагаю, вам не хватает кодировки Base64, прежде чем вы encodeURI.   -  person David    schedule 23.03.2014
comment
@David Я нигде не читал в документации Amazon о кодировке Base64, но вижу, что ваш фрагмент PHP делает это. Как вы поняли, что он должен быть закодирован в Base64?   -  person    schedule 23.03.2014
comment
Я забыл, где я это читал, я закодировал свою библиотеку несколько лет назад и использую ее для всего, так что я больше не беспокоюсь ни о каких низкоуровневых вещах.   -  person David    schedule 23.03.2014


Ответы (3)


Я взломал ваш код, и у меня все заработало.

function sha256(stringToSign, secretKey) {
  var hex = CryptoJS.HmacSHA256(stringToSign, secretKey);
  return hex.toString(CryptoJS.enc.Base64);
} 

function timestamp() {
    var date = new Date();
    var y = date.getUTCFullYear().toString();
    var m = (date.getUTCMonth() + 1).toString();
    var d = date.getUTCDate().toString();
    var h = date.getUTCHours().toString();
    var min = date.getUTCMinutes().toString();
    var s = date.getUTCSeconds().toString();

    if(m.length < 2) { m = "0" + m; }
    if(d.length < 2) { d = "0" + d; }
    if(h.length < 2) { h = "0" + h; }
    if(min.length < 2) { min = "0" + min; }
    if(s.length < 2) { s = "0" + s}

    var date = y + "-" + m + "-" + d;
    var time = h + ":" + min + ":" + s;
    return date + "T" + time + "Z";
}

function getAmazonItemInfo(barcode) {
    var PrivateKey = "";
    var PublicKey = "";
    var AssociateTag = "";

    var parameters = [];
    parameters.push("AWSAccessKeyId=" + PublicKey);
    parameters.push("ItemId=" + barcode);
    parameters.push("Operation=ItemLookup");
    parameters.push("Service=AWSECommerceService");
    parameters.push("Timestamp=" + encodeURIComponent(timestamp()));
    parameters.push("Version=2011-08-01");
parameters.push("AssociateTag=" + AssociateTag);

    parameters.sort();
    var paramString = parameters.join('&');

    var signingKey = "GET\n" + "webservices.amazon.com\n" + "/onca/xml\n" + paramString

    var signature = sha256(signingKey,PrivateKey);
        signature = encodeURIComponent(signature);

    var amazonUrl =  "http://webservices.amazon.com/onca/xml?" + paramString + "&Signature=" + signature;
    console.log(amazonUrl);
}

Заголовок Javascript, который я использовал для справки.

<script src="hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/enc-base64-min.js"></script>
<script src="amazon.js"></script>

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

За то, что я сделал, чтобы это исправить (насколько я помню).

  1. Параметры должны быть в алфавитном порядке. Я поместил их в массив, а затем отсортировал. Я следую за этим соединением с амперсандом.

  2. Я изменил функцию sha256, чтобы она возвращала base64 RAW sha256. Раньше он возвращал шестнадцатеричные биты в нижнем регистре, что неверно.

  3. Я собирался добавить base64 перед кодированием, но sha256 теперь обрабатывает всю подпись.

  4. Неверный формат даты. Он возвращал временную метку эпохи вместо строковой временной метки. Я взломал простую опцию временной метки.

    Этот код требует, чтобы вы также включили библиотеку Base64 для CryptoJS.

person David    schedule 23.03.2014
comment
Это выглядит хорошо, но я специально ищу решение для JavaScript. - person ; 23.03.2014
comment
Обновлено до Javascript. - person David; 23.03.2014

Используйте эту библиотеку Node.js для AWS. Он даже включает пример специально для Продукта. Рекламный API.

person Community    schedule 23.03.2014

Основываясь на отличном ответе Дэвида, я внес некоторые изменения. В приведенном ниже решении используются moment.js и crytpo-js и может использоваться для поиска элементов по ключевому слову. Я использовал блокнот amazon, чтобы создать целевой вызов. Несколько вещей, которые я заметил:

  • Блокнот должен находиться в том же месте, что и ваша партнерская учетная запись, ".com" " .co.uk» и т. д.
  • Конечная точка, в которую вы звоните, должна быть в той же стране, что и ваша учетная запись партнера.
  • Используемая вами отметка времени должна соответствовать местному времени в стране, в которой зарегистрирована ваша учетная запись.

const getAmazonItemInfo = (keywords) => {

  let date = moment().startOf().add(-9, 'hours').format("YYYY-MM-DDThh:mm:ss.000") + 'Z'
  let SecretKey = "GENERATED_IN_AFFILATES_ACCOUNT";
  let AccessKey = "GENERATED_IN_AFFILATES_ACCOUNT";
  let AssociateTag = "FOUND_IN_AFFILATES_ACCOUNT";
  let parameters = [];
  let url = 'webservices.amazon.co.uk' // UK account
  //let url = 'webservices.amazon.com'// US account

  parameters.push("AWSAccessKeyId=" + AccessKey);
  parameters.push("Keywords=" + keywords);
  parameters.push("Operation=ItemSearch");
  parameters.push("SearchIndex=All");
  parameters.push("ResponseGroup=" + encodeURIComponent('Images,ItemAttributes,Offers'));
  parameters.push("Service=AWSECommerceService");
  parameters.push("Timestamp=" + encodeURIComponent(date));
  parameters.push("AssociateTag=" + AssociateTag);
  parameters.sort();

  let paramString = parameters.join('&');
  let string_to_sign = "GET\n" + url + "\n" + "/onca/xml\n" + paramString

  let signature = CryptoJS.HmacSHA256(string_to_sign, SecretKey);
  signature = CryptoJS.enc.Base64.stringify(signature);

  let amazonUrl = "http://" + url + "/onca/xml?" + paramString + "&Signature=" + signature;
  return amazonUrl;
}

let keywords = 'iphone'
console.log(getAmazonItemInfo(keywords))

person Jason Allshorn    schedule 09.11.2017