Netsuite OAuth 不工作

问题描述 投票:0回答:4

我尝试实现 Netsuite 的 OAuth 示例,如下所示:https://netsuite.custhelp.com/app/answers/detail/a_id/42165。我已将其直接发布在下面,因此如果您不想要,则不必访问该页面。

不幸的是,它不起作用。我知道我有正确的令牌和消费者密钥和秘密,以及正确的帐户 ID。但这给了我一个很好的错误:

{"error" : {"code" : "INVALID_LOGIN_ATTEMPT", "message" : "Invalid login attempt."}}

我可以查看我的登录审核,发现它说“签名无效”。但代码本身看起来不错,并且是由 Netsuite 提供的。 我还在 Node.JS 中尝试了一些方法,但还没有让它们工作。关于我下一步应该朝哪个方向走有什么建议吗?

import oauth2 as oauth import requests import time url = "https://rest.netsuite.com/app/site/hosting/restlet.nl?script=992&deploy=1" token = oauth.Token(key="080eefeb395df81902e18305540a97b5b3524b251772adf769f06e6f0d9dfde5", secret="451f28d17127a3dd427898c6b75546d30b5bd8c8d7e73e23028c497221196ae2") consumer = oauth.Consumer(key="504ee7703e1871f22180441563ad9f01f3f18d67ecda580b0fae764ed7c4fd38", secret="b36d202caf62f889fbd8c306e633a5a1105c3767ba8fc15f2c8246c5f11e500c") http_method = "GET" realm="ACCT123456" params = { 'oauth_version': "1.0", 'oauth_nonce': oauth.generate_nonce(), 'oauth_timestamp': str(int(time.time())), 'oauth_token': token.key, 'oauth_consumer_key': consumer.key } req = oauth.Request(method=http_method, url=url, parameters=params) signature_method = oauth.SignatureMethod_HMAC_SHA1() req.sign_request(signature_method, consumer, token) header = req.to_header(realm) headery = header['Authorization'].encode('ascii', 'ignore') headerx = {"Authorization": headery, "Content-Type":"application/json"} print(headerx) conn = requests.get("https://rest.netsuite.com/app/site/hosting/restlet.nl?script=992&deploy=1",headers=headerx) print(conn.text)

我还实现了一些 Node.JS 示例(也没有工作)。这是其中之一(CryptoJS HMAC-SHA1 和 HMAC-SHA256 位于顶部,然后是 oauth-1.0a 库,然后是 Netsuite 提供的代码,经过一些小更改即可使其工作(添加了一个 hash_function,重命名为“public”) ' 到 '键'):

/* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2)b[f>>>3]|=parseInt(a.substr(f, 2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}}, r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this); a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a, d)).finalize(b)}}});var s=e.algo={};return e}(Math); (function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^ g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})(); (function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++)n[h]^=1549556828,j[h]^=909522486;p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d= this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})(); /* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push(4294967296*h.random()|0);return new r.init(c,a)}}),l=f.enc={},k=l.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b, 2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}}, u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);c.sigBytes-=b}return new r.init(g,b)},clone:function(){var a=m.clone.call(this); a._data=this._data.clone();return a},_minBufferSize:0});g.Hasher=u.extend({cfg:m.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){u.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new t.HMAC.init(a, d)).finalize(c)}}});var t=f.algo={};return f}(Math); (function(h){for(var s=CryptoJS,f=s.lib,g=f.WordArray,q=f.Hasher,f=s.algo,m=[],r=[],l=function(a){return 4294967296*(a-(a|0))|0},k=2,n=0;64>n;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]= c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes; d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math); (function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j<h;j++)k[j]^=1549556828,n[j]^=909522486;r.sigBytes=l.sigBytes=m;this.reset()},reset:function(){var f=this._hasher;f.reset();f.update(this._iKey)},update:function(f){this._hasher.update(f);return this},finalize:function(f){var g= this._hasher;f=g.finalize(f);g.reset();return g.finalize(this._oKey.clone().concat(f))}})})(); //oauth-1.0a if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') { module.exports = OAuth; } /** * Constructor * @param {Object} opts consumer key and secret */ function OAuth(opts) { if(!(this instanceof OAuth)) { return new OAuth(opts); } if(!opts) { opts = {}; } if(!opts.consumer) { throw new Error('consumer option is required'); } this.consumer = opts.consumer; this.nonce_length = opts.nonce_length || 32; this.version = opts.version || '1.0'; this.parameter_seperator = opts.parameter_seperator || ', '; this.realm = opts.realm; if(typeof opts.last_ampersand === 'undefined') { this.last_ampersand = true; } else { this.last_ampersand = opts.last_ampersand; } // default signature_method is 'PLAINTEXT' this.signature_method = opts.signature_method || 'PLAINTEXT'; if(this.signature_method == 'PLAINTEXT' && !opts.hash_function) { opts.hash_function = function(base_string, key) { return key; } } if(!opts.hash_function) { throw new Error('hash_function option is required'); } this.hash_function = opts.hash_function; this.body_hash_function = opts.body_hash_function || this.hash_function; } /** * OAuth request authorize * @param {Object} request data * { * method, * url, * data * } * @param {Object} key and secret token * @return {Object} OAuth Authorized data */ OAuth.prototype.authorize = function(request, token) { var oauth_data = { oauth_consumer_key: this.consumer.key, oauth_nonce: this.getNonce(), oauth_signature_method: this.signature_method, oauth_timestamp: this.getTimeStamp(), oauth_version: this.version }; if(!token) { token = {}; } if(token.key !== undefined) { oauth_data.oauth_token = token.key; } if(!request.data) { request.data = {}; } if(request.includeBodyHash) { oauth_data.oauth_body_hash = this.getBodyHash(request, token.secret) } oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data); return oauth_data; }; /** * Create a OAuth Signature * @param {Object} request data * @param {Object} token_secret key and secret token * @param {Object} oauth_data OAuth data * @return {String} Signature */ OAuth.prototype.getSignature = function(request, token_secret, oauth_data) { return this.hash_function(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret)); }; /** * Create a OAuth Body Hash * @param {Object} request data */ OAuth.prototype.getBodyHash = function(request, token_secret) { var body = typeof request.data === 'string' ? request.data : JSON.stringify(request.data) if (!this.body_hash_function) { throw new Error('body_hash_function option is required'); } return this.body_hash_function(body, this.getSigningKey(token_secret)) }; /** * Base String = Method + Base Url + ParameterString * @param {Object} request data * @param {Object} OAuth data * @return {String} Base String */ OAuth.prototype.getBaseString = function(request, oauth_data) { return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data)); }; /** * Get data from url * -> merge with oauth data * -> percent encode key & value * -> sort * * @param {Object} request data * @param {Object} OAuth data * @return {Object} Parameter string data */ OAuth.prototype.getParameterString = function(request, oauth_data) { var base_string_data; if (oauth_data.oauth_body_hash) { base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.deParamUrl(request.url)))); } else { base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url))))); } var data_str = ''; //base_string_data to string for(var i = 0; i < base_string_data.length; i++) { var key = base_string_data[i].key; var value = base_string_data[i].value; // check if the value is an array // this means that this key has multiple values if (value && Array.isArray(value)){ // sort the array first value.sort(); var valString = ""; // serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2 value.forEach((function(item, i){ valString += key + '=' + item; if (i < value.length){ valString += "&"; } }).bind(this)); data_str += valString; } else { data_str += key + '=' + value + '&'; } } //remove the last character data_str = data_str.substr(0, data_str.length - 1); return data_str; }; /** * Create a Signing Key * @param {String} token_secret Secret Token * @return {String} Signing Key */ OAuth.prototype.getSigningKey = function(token_secret) { token_secret = token_secret || ''; if(!this.last_ampersand && !token_secret) { return this.percentEncode(this.consumer.secret); } return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret); }; /** * Get base url * @param {String} url * @return {String} */ OAuth.prototype.getBaseUrl = function(url) { return url.split('?')[0]; }; /** * Get data from String * @param {String} string * @return {Object} */ OAuth.prototype.deParam = function(string) { var arr = string.split('&'); var data = {}; for(var i = 0; i < arr.length; i++) { var item = arr[i].split('='); // '' value item[1] = item[1] || ''; // check if the key already exists // this can occur if the QS part of the url contains duplicate keys like this: ?formkey=formvalue1&formkey=formvalue2 if (data[item[0]]){ // the key exists already if (!Array.isArray(data[item[0]])) { // replace the value with an array containing the already present value data[item[0]] = [data[item[0]]]; } // and add the new found value to it data[item[0]].push(decodeURIComponent(item[1])); } else { // it doesn't exist, just put the found value in the data object data[item[0]] = decodeURIComponent(item[1]); } } return data; }; /** * Get data from url * @param {String} url * @return {Object} */ OAuth.prototype.deParamUrl = function(url) { var tmp = url.split('?'); if (tmp.length === 1) return {}; return this.deParam(tmp[1]); }; /** * Percent Encode * @param {String} str * @return {String} percent encoded string */ OAuth.prototype.percentEncode = function(str) { return encodeURIComponent(str) .replace(/\!/g, "%21") .replace(/\*/g, "%2A") .replace(/\'/g, "%27") .replace(/\(/g, "%28") .replace(/\)/g, "%29"); }; /** * Percent Encode Object * @param {Object} data * @return {Object} percent encoded data */ OAuth.prototype.percentEncodeData = function(data) { var result = {}; for(var key in data) { var value = data[key]; // check if the value is an array if (value && Array.isArray(value)){ var newValue = []; // percentEncode every value value.forEach((function(val){ newValue.push(this.percentEncode(val)); }).bind(this)); value = newValue; } else { value = this.percentEncode(value); } result[this.percentEncode(key)] = value; } return result; }; /** * Get OAuth data as Header * @param {Object} oauth_data * @return {String} Header data key - value */ OAuth.prototype.toHeader = function(oauth_data) { var sorted = this.sortObject(oauth_data); var header_value = 'OAuth '; if (this.realm) { header_value += 'realm="' + this.realm + '"' + this.parameter_seperator; } for(var i = 0; i < sorted.length; i++) { if (sorted[i].key.indexOf('oauth_') !== 0) continue; header_value += this.percentEncode(sorted[i].key) + '="' + this.percentEncode(sorted[i].value) + '"' + this.parameter_seperator; } return { Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars }; }; /** * Create a random word characters string with input length * @return {String} a random word characters string */ OAuth.prototype.getNonce = function() { var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var result = ''; for(var i = 0; i < this.nonce_length; i++) { result += word_characters[parseInt(Math.random() * word_characters.length, 10)]; } return result; }; /** * Get Current Unix TimeStamp * @return {Int} current unix timestamp */ OAuth.prototype.getTimeStamp = function() { return parseInt(new Date().getTime()/1000, 10); }; ////////////////////// HELPER FUNCTIONS ////////////////////// /** * Merge object * @param {Object} obj1 * @param {Object} obj2 * @return {Object} */ OAuth.prototype.mergeObject = function(obj1, obj2) { obj1 = obj1 || {}; obj2 = obj2 || {}; var merged_obj = obj1; for(var key in obj2) { merged_obj[key] = obj2[key]; } return merged_obj; }; /** * Sort object by key * @param {Object} data * @return {Array} sorted array */ OAuth.prototype.sortObject = function(data) { var keys = Object.keys(data); var result = []; keys.sort(); for(var i = 0; i < keys.length; i++) { var key = keys[i]; result.push({ key: key, value: data[key], }); } return result; }; //NETSUITE'S RESTLET function callRESTlet(request, response) { var remoteAccountID = 'ACCOUNT ID HERE'; var restletUrl = 'https://rest.netsuite.com/app/site/hosting/restlet.nl?script=992&deploy=1'; //user token var token = { key: 'ACCESS KEY HERE', secret: 'ACCESS SECRET HERE' }; //app credentials var oauth = OAuth({ consumer: { key: 'INTEGRATION KEY HERE', secret: 'INTEGRATION SECRET HERE' }, signature_method: 'HMAC-SHA1', hash_function: function(base_string, key) { return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64); } }); var request_data = { url: restletUrl, method: 'GET', data: {} }; var oauth_data = { oauth_consumer_key: oauth.consumer.key, oauth_nonce: oauth.getNonce(), oauth_signature_method: oauth.signature_method, oauth_timestamp: oauth.getTimeStamp(), oauth_version: '1.0', oauth_token: token.key, realm: remoteAccountID }; var headerWithRealm = oauth.toHeader(oauth.authorize(request_data, token)); headerWithRealm.Authorization += ',realm="' + remoteAccountID + '"'; var restResponse = nlapiRequestURL(restletUrl, null, headerWithRealm, null, "GET"); var html = 'Calling: ' + restletUrl + '<br><br>' + 'Generated OAuth header:<br>' + headerWithRealm.Authorization + '<br><br>' + 'Response:<br>' + restResponse.getBody() response.write(html); }


oauth access-token netsuite
4个回答
6
投票
https://www.npmjs.com/package/nsrestlet

在搜索 GitHub 代码提交后,能够让一些代码正常工作。不过bknights的反应真的很好。

这就是我的工作。

假设你安装了 Node.js 和 npm,运行:

npm install request npm install [email protected]

1.0.1 版本非常重要。

一旦你有了这个,这段代码应该可以工作:

/* ================= REQUIRED USER ACCOUNT INFORMATION ============================================== */ var accountID = 'PUT ACCOUNT ID HERE'; var token = { public: 'PUT TOKEN KEY HERE', secret: 'PUB TOKEN SECRET HERE' }; var consumer = { public: 'PUT CONSUMER KEY HERE', secret: 'PUT CONSUMER SECRET HERE' }; //use the full restlet URL, not the rest.netsuite.com URL //for example, https://YOURACCOUNTNUMBER.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=SCRIPTNUMBER&deploy=DEPLOYNUMBER var restlet_url = 'PUT YOUR RESTLET URL HERE'; /* ========================================================================================================= */ //REQUIRED NPM MODULES const request = require('request'); const OAuth = require('oauth-1.0a'); //version 1.0.1, don't do version 1.1.0 //SET UP THE OAUTH OBJECT var oauth = OAuth({ consumer: consumer, signature_method: 'HMAC-SHA256' //you can also use HMAC-SHA1 but HMAC-SHA256 is more secure (supposedly) }); //SET UP THE REQUEST OBJECT var request_data = { url: restlet_url, method: 'POST', }; //GET THE AUTHORIZATION AND STICK IT IN THE HEADER, ALONG WITH THE REALM AND CONTENT-TYPE var authorization = oauth.authorize(request_data, token); var header = oauth.toHeader(authorization); header.Authorization += ', realm="' + accountID + '"'; header['content-type'] = 'application/json'; //MAKE THE REQUEST request({ url: request_data.url, method: request_data.method, headers: header, json: { message: "test123" //this is your payload } }, function(error, response, body) { if(error) { console.log(error); } else { console.log(body); } });

如果有人对此代码有任何疑问,请留下回复,我会尽力提供帮助。


4
投票

https://netsuite.custhelp.com/app/answers/detail/a_id/42171

及其示例来自

https://netsuite.custhelp.com/app/answers/detail/a_id/42172/

我已经在生产中使用了几年并且效果很好。

var Promise = require('bluebird'); var request = require('request'); var crypto = require('crypto'); var OAuth = require('oauth-1.0a'); var Agent = require('https').Agent; //var debug = require('debug')('kotn-ns'); function promiseTry(pSrc, maxTries, minDelay, maxDelay, canRetry){ //NS prone to spurious failures due to overloading return new Promise(function(resolve, reject){ minDelay = minDelay || 0; var delaySize = maxDelay - minDelay; var t = function(){ return Math.floor(Math.random()* delaySize)+ minDelay;}; var firstReason = null; function doRetry(triesLeft){ pSrc().then(function(data){ resolve(data); }).catch(function(reason){ if(!firstReason) firstReason = reason; console.error('in retry error with '+reason.toString()); if(triesLeft && canRetry(reason)) setTimeout(function(){ doRetry(triesLeft-1);}, t()); else reject(firstReason); }); } doRetry(maxTries -1); }); } function hasReason(msg, reasons){ for(var i = 0; i< reasons.length;i++){ if(msg.indexOf(reasons[i]) != -1) return true; } return false; } var agentPool = {}; function getAgent(accountId, tokenId){ var agentKey = accountId+'::'+ tokenId; var agent = agentPool[agentKey]; if(!agent){ console.log('new agent for '+agentKey) agent = new Agent({ keepAlive:false, maxSockets:5 }); agentPool[agentKey] = agent; } return agent; } /** * [RESTHandler description] * @param {options} options {accountId, consumerKey,consumerSecret,tokenId,tokenSecret} */ function RESTHandler(options) { var config = Object.assign({ maxTries:3, minRetryDelay: 800, maxRetryDelay:30000, canRetry: function(reason){ var reasonText = reason.message || JSON.stringify(reason); if(hasReason(reasonText, ['ECONNRESET', 'ESOCKETTIMEDOUT','ETIMEDOUT', 'SSS_REQUEST_LIMIT_EXCEEDED'])) { console.error('retrying because: '+reasonText); return true; } console.error('no retry with: '+reasonText); return false; } }, options); var oauth = OAuth({ consumer: { key: config.consumerKey, secret: config.consumerSecret }, signature_method: 'HMAC-SHA1', parameter_seperator: ',', hash_function: function(base_string, key) { return crypto.createHmac('sha1', key).update(base_string).digest('base64'); } }); var token = { key: config.tokenId, secret: config.tokenSecret }; function makeRequest(url, method, payload) { var requestData = { url: url, method: method }; if(payload){ requestData.body = payload; } var headers = oauth.toHeader(oauth.authorize(requestData, token)); headers.Authorization += ',realm="' + config.accountId + '"'; headers.authorization = headers.Authorization; delete headers.Authorization; headers['content-type'] = 'application/json'; headers['accept'] = 'application/json'; //console.log(JSON.stringify(headers, null, ' ')); requestData.headers = headers; Object.assign(requestData, { pool:getAgent(config.accountId, config.tokenId), timeout : 30000, strictSSL : true }); // requestData.json = true; // return new Promise(function(resolve){ // resolve({'headers' : 'done'}); // }); var processRequest = function(){ return new Promise(function(resolve, reject) { request(requestData, function(error, response, body) { if(error){ console.error('error calling: '+ requestData.url); console.error(error); reject((error instanceof Error) ? error : new Error(JSON.stringify(error))); return; } if(!body || !(/"success"/).test(body)) { console.log(method +' '+ response.statusCode +' '+ url +'\n\t'+body); reject(new Error(body || 'unexpected error')); return; } try{ resolve(JSON.parse(body)); }catch(e){ console.trace(e); reject(e); } }); }); }; return promiseTry(processRequest, config.maxTries, config.maxRetryDelay, config.minRetryDelay, config.canRetry); } return{ get: function(url){ return makeRequest(url, 'GET'); }, put: function(url, data){ return makeRequest(url, 'PUT', data); }, post: function(url, data){ return makeRequest(url, 'POST', data); }, destroy : function(){ //nsAgent.destroy(); } }; } module.exports = RESTHandler;



0
投票
request

替换为其后继者

needle
并删除了现在不需要的
bluebird
const needle = require('needle');
const crypto = require('crypto');
const OAuth = require('oauth-1.0a');
const Agent = require('https').Agent;
const debug = require('debug')('kotn-nso');


function promiseTry(pSrc, maxTries, minDelay, maxDelay, canRetry){ //NS prone to spurious failures due to overloading
    return new Promise((resolve, reject)=>{

        minDelay = minDelay || 0;
        const delaySize = maxDelay - minDelay;
        const t = function(){ return Math.floor(Math.random()* delaySize)+ minDelay;};
        let firstReason = null;

        function doRetry(triesLeft){

            pSrc().then((data)=>{
                resolve(data);
            }).catch((reason)=>{
                if(!firstReason) firstReason = reason;
                console.error('in retry error with '+ triesLeft +' for '+reason.toString());
                if(triesLeft  && canRetry(reason)){
                    setTimeout(()=>{ doRetry(triesLeft-1);}, t());
                } 
                else reject(firstReason);
            });
        }
        doRetry(maxTries -1);
    });
}

function hasReason(msg, reasons){
    for(var i = 0; i< reasons.length;i++){
        if(msg.indexOf(reasons[i]) != -1) return true;
    }
    return false;
}

var agentPool = {};
function getAgent(accountId, maxSockets){
    var agentKey = accountId+'::'; // + tokenId;
    var agent = agentPool[agentKey];
    if(!agent){
        console.log('new agent for '+agentKey);
        agent = new Agent({
            keepAlive:false,
            maxSockets:maxSockets || 2 // one in reserve for slow closers
        });
        agentPool[agentKey] = agent;
    }
    return agent;
}

/**
 * [RESTHandler description]
 * @param {options} options {accountId, consumerKey,consumerSecret,tokenId,tokenSecret,maxSockets}
 */
function RESTHandler(options) {


    const config = Object.assign({
        maxTries:3,
        minRetryDelay: 400,
        maxRetryDelay:30000,
        canRetry: function(reason){
            var reasonText =  reason.message || JSON.stringify(reason);
            if(hasReason(reasonText, ['ECONNRESET', 'ESOCKETTIMEDOUT','ETIMEDOUT', 'SSS_REQUEST_LIMIT_EXCEEDED'])) {
                console.error('retrying because: '+reasonText);
                return true;
            }
            console.error('no retry with: '+reasonText);
            return false;
        }
    }, options);

    const oauth = OAuth({
        consumer: {
            key: config.consumerKey,
            secret: config.consumerSecret
        },
        signature_method: 'HMAC-SHA256',
        parameter_seperator: ',',
        hash_function: function(base_string, key) {
            return crypto.createHmac('sha256', key).update(base_string).digest('base64');
        }
    });
    const token = {
        key: config.tokenId,
        secret: config.tokenSecret
    };

    function makeRequest(url, method, payload) {
        debug(method +' '+ JSON.stringify(url));
        var requestData = {
            url: url,
            method: method
        };
        if(payload){
            requestData.body = payload;
        }
        
        var headers = oauth.toHeader(oauth.authorize(requestData, token));
        headers.Authorization += ',realm="' + config.accountId + '"';
        headers.authorization = headers.Authorization;
        delete headers.Authorization;
        headers['content-type'] = 'application/json';
        headers['accept'] = 'application/json';

        //console.log(JSON.stringify(headers, null, '  '));
        const options = {
            headers:headers,
            agent:getAgent(config.accountId, config.maxSockets), 
            timeout : 30000,
            strictSSL : true,
            time:false
        };
    
        var processRequest = function(){
            var headers = null;
            return needle(method.toLowerCase(), url, payload, options).then(resp=>{
                debug(resp.statusCode +' ' + JSON.stringify(resp.headers, null, ' '));
                headers = resp.headers;

                if(resp.statusCode != 200 && resp.statusCode != 201){
                    throw new Error(resp.statusCode +': '+ (resp.body || 'unexpected error'));
                }
                if(!resp.body) throw new Error('Unexpected Response');
                return resp.body;
            }).catch(error=>{
                if(headers){
                    debug('Error returned with ' + JSON.stringify(headers));
                }
                const msg = error instanceof Error ? error.message : JSON.stringify(error);
                console.error(method +' error on '+ url + ', '+ msg);
                throw (error instanceof Error) ? error : new Error(msg);
            });
        };
        return promiseTry(processRequest, config.maxTries, config.maxRetryDelay, config.minRetryDelay, config.canRetry);
    }

    return{
        get: function(url){
            return makeRequest(url, 'GET');
        },
        put: function(url, data){
            return makeRequest(url, 'PUT', data);
        },
        post: function(url, data){
            return makeRequest(url, 'POST', data);
        },
        destroy : function(){
            //nsAgent.destroy();
        }
    };
}
module.exports = RESTHandler;



0
投票
什么是 NetSuite API 以及如何设置它?

阅读本文

© www.soinside.com 2019 - 2024. All rights reserved.