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;
|
}
|
|
exports.getSignInternal = function getSignInternal(bizParams, body, httpHeaders, secret, charset) {
|
charset = charset || "utf-8";
|
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;
|
};
|
|
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;
|
};
|