簡體   English   中英

使用帶有 GET 過濾器的 Magento Rest API 時出現 oauth_signature 無效錯誤

[英]oauth_signature invalid error while using Magento Rest API with GET filters

當我使用它的 GET 過濾器時,我很難從 Magento REST API 獲取訂單http://localhost/magento/api/rest/orders/?filter[1][attribute]=entity_id&filter[1][gt] =70&頁=1&限制=100

它給出了一個“錯誤”:[{“代碼”:401,“消息”:“oauth_problem = signature_invalid”}]

當我嘗試使用 REST 客戶端(如 Postman)訪問相同的 API 端點時,我得到了所需的結果 JSON。

我懷疑過濾器查詢中的方括號可能會導致生成 Oauth 簽名時出現問題。 所有沒有 GET 過濾器的端點都工作正常。 我正在使用 Request 節點模塊發出帶有 oauth 標頭的 GET 請求。

是否有任何修復來避免簽名無效錯誤?

問題出在我用來生成OAuth簽名的Request節點模塊中。它沒有考慮URL中的方括號。 我修改了模塊中包含方括號的代碼。 更改OAuth簽名生成方法為我修復了它

因為我花了一點時間才弄清楚如何做到這一點,我想我會傳遞我學到的東西。 我的目標是向Magento的REST API發出單一請求,以返回具有特定訂單狀態的訂單。 在看到最后一行之前,查看GET過濾器文檔頁面並沒有什么用處。 以下是我提出的有效的請求:

http://magentohost.com/api/rest/orders?filter[1][attribute]=status&filter[1][in][1]=pending&filter[1][in][2]=processing上述請求將獲得您是狀態為“待定”或“處理”的所有訂單的列表。

參考: http//magentoforce.com/2014/08/magento-rest-api-multiple-in-get-filters/

對於任何想要回答這個問題的人來說,這對我有用:

const axios = require('axios');
const crypto = require('crypto');

exports.handler = async (event) => {
  let base_uri = 'https://YOUR_URL.com'; (ANYTHING UP TO THE /PATH)
  const consumer_key = '';
  const token_key = '';
  const consumer_secret = '';
  const token_secret = '';
==> CHANGE THE QUERY PARAMS TO WHATEVER YOU HAVE <==
  const queryParameters = {
    'searchCriteria[filterGroups][0][filters][0][field]': 'sku',
    'searchCriteria[filterGroups][0][filters][0][condition_type]': 'eq',
    'searchCriteria[filterGroups][0][filters][0][value]': 'aaaaaaaaa',
  };
  const timestamp = Math.floor(Date.now() / 1000);
  const nonce = await generateNonce();

  ==> ADD IN YOUR PATH, IT WILL BE APPENDED TO THE BASE URL <==
  const path = 'products';

  base_uri = `${base_uri}/${path}`;

  const reqsign256 = hmacsign256('GET', base_uri, { ...queryParameters, oauth_consumer_key: consumer_key, oauth_nonce: nonce, oauth_signature_method: 'HMAC-SHA256', oauth_timestamp: timestamp, oauth_token: token_key, oauth_version: '1.0' }, consumer_secret, token_secret);

  const config = {
    method: 'get',
    url: base_uri,
    headers: {
      Authorization: `OAuth oauth_consumer_key="${consumer_key}",oauth_token="${token_key}",oauth_signature_method="HMAC-SHA256",oauth_timestamp="${timestamp}",oauth_nonce="${nonce}",oauth_version="1.0",oauth_signature="${reqsign256}"`
    },
    params: queryParameters,
  };

  let response = {};

  return await axios(config)
    .then(async (response) => {
      response = {
        statusCode: 200,
        body: JSON.stringify(response.data),
      };
      return response;
    })
    .catch((error) => {
      response = {
        statusCode: 200,
        body: JSON.stringify(error.message),
      };
      return response;
    });
};

/**
 *
 * @returns A random string of 11 characters
 */
function generateNonce() {
  let nonce = '';
  const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

  for (let i = 0; i < 11; i++) {
    nonce += possible.charAt(Math.floor(Math.random() * possible.length));
  }

  return nonce;
}

/**
 *
 * @param key
 * @param body
 * @param algorithm
 * @description This is generating the signature using the imported crypto package.
 * @returns The generated signature;
 */
function sha(key, body, algorithm) {
  return crypto.createHmac(algorithm, key).update(body).digest('base64');
}

/**
 *
 * @param str
 * @description The rfc3986 function takes a string as input and returns its encoded representation as per the rules specified in RFC 3986
 * for URI (Uniform Resource Identifier) component encoding. It does this by first using encodeURIComponent to encode the string,
 * which replaces certain characters with their percent-encoded representations. It then replaces characters such as !, *, (, ), and '
 * with their respective percent-encoded representations, which are specified in RFC 3986 as being reserved characters that must be percent-encoded.
 * @returns returns the encoded str value
 */
function rfc3986(str) {
  return encodeURIComponent(str).replace(/!/g, '%21').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/'/g, '%27');
}

/**
 *
 * @param obj
 * @description The map function takes an object as input and returns an array of its key-value pairs.
 * It does this by iterating through the properties of the object using a for...in loop, and for each property, it checks its value.
 * If the value is an array, it pushes an array with the property key and each value of the array into the result array.
 * If the value is an object, it pushes an array with a string representation of the property key concatenated with the property name in square brackets
 * and the property value into the result array.
 * If the value is neither an array nor an object, it pushes an array with the property key and value into the result array.
 * Finally, it returns the result array.
 * @returns arr
 */
function map(obj) {
  let key,
    val,
    arr = [];
  for (key in obj) {
    val = obj[key];
    if (Array.isArray(val)) for (let i = 0; i < val.length; i++) arr.push([key, val[i]]);
    else if (typeof val === 'object') for (let prop in val) arr.push([key + '[' + prop + ']', val[prop]]);
    else arr.push([key, val]);
  }
  return arr;
}

/**
 *
 * @param a
 * @param b
 * @description Used to sort the oauth paramters into ascending order -- this is required for oauth1 to work.
 * @returns the comparison result
 */
function compare(a, b) {
  return a > b ? 1 : a < b ? -1 : 0;
}

/**
 *
 * @param httpMethod
 * @param base_uri
 * @param params
 * @description
 * 1.  First, the name and value of each parameter are encoded
 * 2.  The parameters are sorted by name, using ascending byte value ordering. If two or more parameters share the same name, they are sorted by their value.
 * 3.  The name of each parameter is concatenated to its corresponding value using an "=" character (ASCII code 61) as a separator, even if the value is empty.
 * for example in this case it is assigning oauth_nonce = 'foo' or oauth_signature_method = 'HMAC-SHA256' etc
 * 4.  The sorted name/value pairs are concatenated together into single string by using an "&" character (ASCII code 38) as separator.
 * The final output will be something like this:
 * GET&https%3A%2F%2Fstaging2.ospreylondon.com%2Frest%2FV1%2Fproducts&oauth_consumer_key%3Dxxx%26oauth_nonce%3xxxoauth_signature_method%3DHMAC-SHA256%26oauth_timestamp%3Dxxx%26oauth_token%3xxx%26oauth_version%3D1.0%26 ==> There will also be any url paramaters added at the end.
 * @returns base url
 */
function generateBase(httpMethod, base_uri, params) {
  let normalized = map(params)
    // step 1
    .map(function (p) {
      return [rfc3986(p[0]), rfc3986(p[1] || '')];
    })
    // step 2
    .sort(function (a, b) {
      return compare(a[0], b[0]) || compare(a[1], b[1]);
    })
    //step 3
    .map(function (p) {
      return p.join('=');
    })
    //step 4
    .join('&');

  let base = [rfc3986(httpMethod ? httpMethod.toUpperCase() : 'GET'), rfc3986(base_uri), rfc3986(normalized)].join('&');

  return base;
}

/**
 *
 * @param httpMethod
 * @param base_uri
 * @param params
 * @param consumer_secret
 * @param token_secret
 * @description This takes the paramaters passed in, creates a base uri, creates a key (consumer secret and token secret with & between them)
 * @returns this then returns the result of the sha method ==> this is the signature used in the request.
 */
function hmacsign256(httpMethod, base_uri, params, consumer_secret, token_secret) {
  let base = generateBase(httpMethod, base_uri, params);
  let key = [consumer_secret || '', token_secret || ''].map(rfc3986).join('&');

  return sha(key, base, 'sha256');
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM