Как правильно подписать запрос GET к ItemLookup Amazon, используя только клиентский 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>
Полезные ссылки:
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>
Вам нужно будет изменить его части, потому что я изменил некоторые параметры и не ссылаюсь на ваш объект приложения.
За то, что я сделал, чтобы это исправить (из того, что я могу вспомнить).
Параметры должны быть в алфавитном порядке. Я поместил их в массив, а затем отсортировать их. Я следую за этим соединением с амперсандом.
Я изменил функцию sha256, чтобы она возвращала base64 RAW sha256. Раньше он возвращал гексбиты в нижнем регистре, что не правильно.
Я собирался добавить base64 перед кодированием, но sha256 теперь обрабатывает все подписи.
Формат даты был неверным. Он возвращал метку времени эпохи вместо метки времени строки. Я взломал простой вариант отметки времени.
Этот код требует, чтобы вы также включили библиотеку Base64 для CryptoJS.
Используйте эту библиотеку Node.js для AWS. Он даже включает пример специально для API рекламы продукта.
Опираясь на замечательный ответ Дэвида, я сделал несколько поправок. В приведенном ниже решении используются 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))