sync-alisdk
Version:
Taobao Open API & Message Client.
156 lines (141 loc) • 3.98 kB
JavaScript
var util = require('./topUtil.js');
var iconv = require('iconv-lite');
var URL = require('url');
var urlencode = require('urlencode');
var ipFileds = ["X-Real-IP", "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"];
String.prototype.contains = function(target){
return this.indexOf(target) > -1;
}
/**
* 校验SPI请求签名,不支持带上传文件的HTTP请求。
*
* @param bizParams 业务参数
* @param httpHeaders http头部信息
* @param secret APP密钥
* @param charset 目标编码
* @return boolean
*/
exports.checkSignForSpi = function checkSignForSpi(url,body,httpHeaders,secret) {
var ctype = httpHeaders['content-type'];
if(!ctype){
ctype = httpHeaders['Content-Type'];
}
if(!ctype){
return false;
}
var charset = this.getResponseCharset(ctype);
var urlParams = URL.parse(url).query.split("&");
var bizParams = buildBizParams(urlParams);
return checkSignInternal(bizParams,body,httpHeaders,secret,charset);
}
function buildBizParams(urlParams){
var bizParams = {};
for(var i =0; i < urlParams.length; i++){
var params = urlParams[i].split("=");
bizParams[params[0]] = params[1];
}
return bizParams;
}
/**
* 检查发起SPI请求的来源IP是否是TOP机房的出口IP。
*
* @param request HTTP请求
* @param topIpList TOP网关IP出口地址段列表,通过taobao.top.ipout.get获得
*
* @return boolean true表达IP来源合法,false代表IP来源不合法
*/
exports.checkRemoteIp = function checkRemoteIp(httpHeaders,topIpList){
var ip = null;
for(var i = 0; i < ipFileds.length; i++){
var realIp = httpHeaders[ipFileds[i]];
if(realIp && 'unknown' != realIp.toLowerCase()){
ip = realIp;
break;
}
}
if(ip){
for(var i = 0; i < topIpList.length; i++) {
if(ip == topIpList[i]){
return true;
}
}
}
return false;
}
/**
* 检查SPI请求到达服务器端是否已经超过指定的分钟数,如果超过则拒绝请求。
*
* @return boolean true代表不超过,false代表超过。
*/
exports.checkTimestamp = function checkTimestamp(bizParams,minutes){
var timestamp = bizParams['timestamp'];
if(timestamp){
var remove = new Date(timestamp).getTime();
var local = new Date().getTime();
return (local - remove) <= minutes * 60 * 1000;
}
return false;
}
function arrayConcat(bizParams,signHttpParams){
if(signHttpParams){
for(var i=0; i < signHttpParams.length; i++){
bizParams[signHttpParams[i].key] = signHttpParams[i].value;
}
}
}
function checkSignInternal(bizParams,body,httpHeaders,secret,charset){
var remoteSign = bizParams['sign'];
arrayConcat(bizParams,getHeaderMap(httpHeaders));
var sorted = Object.keys(bizParams).sort();
var bastString = secret;
var localSign ;
for (var i = 0, l = sorted.length; i < l; i++) {
var k = sorted[i];
var value = bizParams[k];
if(k == 'sign'){
continue;
}
value = urlencode.decode(bizParams[k],charset);
if(k == 'timestamp'){
value = value.replace('+',' ');
}
k = iconv.encode(k,charset);
bastString += k;
bastString += value;
}
if(body){
bastString += body;
}
bastString += secret;
var buffer = iconv.encode(bastString,charset);
localSign = util.md5(buffer).toUpperCase();
return localSign == remoteSign;
}
function getHeaderMap(httpHeaders){
var resultMap = {};
var signList = httpHeaders['top-sign-list'];
if(signList){
var targetKeys = signList.split(",");
targetKeys.forEach(function(target){
resultMap[target] = httpHeaders[target];
})
}
return resultMap;
}
exports.getResponseCharset = function getResponseCharset(ctype){
var charset = 'UTF-8';
if(ctype){
var params = ctype.split(";");
for(var i = 0; i < params.length; i++){
var param = params[i].trim();
if(param.startsWith('charset')){
var pair = param.split("=");
charset = pair[1].trim().toUpperCase();
}
}
}
if(charset && charset.toLowerCase().startsWith('GB')){
charset = "GBK";
}
return charset;
}