UNPKG

@kikitrade/api-gateway-client

Version:

alibaba cloud open api gateway two way communication library

138 lines (137 loc) 5.01 kB
import { parse } from 'url'; import CryptoJS from 'crypto-js'; import { v4 } from 'uuid'; const Content_Type_Form_Data = 'application/x-www-form-urlencoded; charset=utf-8'; const Content_Type_Json_Data = 'application/octet-stream; charset=utf-8'; const Accept_JSON = 'application/json; charset=utf-8'; let seq = 1; function seqInc() { return seq++; } class Util { constructor(key, secret, stage) { this.appKey = key; this.appSecret = Buffer.from(secret, 'utf8'); this.stage = stage; } static buildUrl(parsedUrl, data = {}) { let toStringify = Object.assign(parsedUrl.query, data); let result = parsedUrl.pathname; if (Object.keys(toStringify).length) { let list = this.buildParameters(toStringify); result += '?' + list.join('&'); } return result; } static buildParameters(toStringify) { let keys = Object.keys(toStringify).sort(); let list = new Array(keys.length); for (let i = 0; i < keys.length; i++) { let key = keys[i]; if (toStringify[key] !== undefined && toStringify[key] !== null && ('' + toStringify[key])) { list[i] = `${key}=${toStringify[key]}`; } else { list[i] = `${key}`; } } return list; } buildStringToSign(method, headers, signedHeadersStr, url, data = {}) { // accept, contentMD5, contentType, const lf = '\n'; let list = [method, lf]; const accept = headers['accept']; if (accept) { list.push(accept); } list.push(lf); const contentMD5 = headers['content-md5']; if (contentMD5) { list.push(contentMD5); } list.push(lf); const contentType = (headers['content-type'] || ''); if (contentType) { list.push(contentType); } list.push(lf); const date = headers['date']; if (date) { list.push(date); } list.push(lf); if (signedHeadersStr) { list.push(signedHeadersStr); list.push(lf); } if (contentType.startsWith(Content_Type_Form_Data)) { list.push(Util.buildUrl(url, data)); } else { list.push(Util.buildUrl(url)); } return list.join(''); } sign(stringToSign) { return CryptoJS.HmacSHA256(stringToSign, this.appSecret).toString(CryptoJS.enc.Base64); } static md5(content) { return CryptoJS.MD5(content).toString(CryptoJS.enc.Base64); } getSignHeaderKeys(headers) { const keys = Object.keys(headers).sort(); const signKeys = []; for (let i = 0; i < keys.length; i++) { const key = keys[i]; // x-ca- 开头的header或者指定的header if (key.startsWith('x-ca-')) { signKeys.push(key); } } // 按字典序排序 return signKeys.sort(); } buildHeaders(headers = {}, extra = {}) { return Object.assign({ 'ca_version': ['1'], 'x-ca-timestamp': [Date.now()], 'x-ca-key': [this.appKey], 'x-ca-nonce': [v4()], 'x-ca-seq': [Number(seqInc()).toString()], 'x-ca-stage': [this.stage], }, headers, extra); } getSignedHeadersString(signHeaders, headers) { let list = []; for (let i = 0; i < signHeaders.length; i++) { let key = signHeaders[i]; list.push(key + ':' + headers[key]); } return list.join('\n'); } // normal post or get to ws signed request createSignedRequestHeaders(method, url, requestHeaders, contentType, acceptType, webSocketApiType, body, deviceId) { // 小写化,合并之后的headers let headers = this.buildHeaders(requestHeaders, { 'content-type': [contentType], 'accept': [acceptType] }); if (webSocketApiType) { headers['x-ca-websocket_api_type'] = [webSocketApiType]; } if (deviceId) { headers['x-ca-deviceid'] = [deviceId]; } let requestContentType = headers['content-type'] || ''; //stream form if (method === 'POST' && !requestContentType.startsWith(Content_Type_Form_Data) && body) { headers['content-md5'] = [Util.md5(body)]; } let signHeaderKeys = this.getSignHeaderKeys(headers); headers['x-ca-signature-headers'] = [signHeaderKeys.join(',')]; let signedHeadersStr = this.getSignedHeadersString(signHeaderKeys, headers); let parsedUrl = parse(url, true); let stringToSign = this.buildStringToSign(method, headers, signedHeadersStr, parsedUrl, body); headers['x-ca-signature'] = [this.sign(stringToSign)]; return headers; } } export { Util, seqInc, Content_Type_Form_Data, Content_Type_Json_Data, Accept_JSON };