Authored by 陈峰

Merge branch 'feature/risk' into 'gray'

risk



See merge request !254
... ... @@ -134,6 +134,7 @@ const logger = global.yoho.logger;
// dispatcher
try {
const setYohoData = require('./doraemon/middleware/set-yoho-data');
const riskManagement = require('./doraemon/middleware/risk-management');
const htaccess = require('./doraemon/middleware/htaccess');
const subDomain = require('./doraemon/middleware/sub-domain');
const mobileRefer = require('./doraemon/middleware/mobile-refer');
... ... @@ -149,6 +150,7 @@ try {
// YOHO 前置中间件
app.use(setYohoData());
app.use(riskManagement());
app.use(htaccess());
app.use(subDomain());
app.use(mobileRefer());
... ...
... ... @@ -31,7 +31,7 @@ const isHuman = (req, res) => {
delete req.session.apiLimitValidate;
logger.warn('isHuman', remoteIp);
return robotCheckService.removeBlack(remoteIp, apiLimitValidate).then(() => {
return robotCheckService.removeBlack(remoteIp, apiLimitValidate, req.headers.referer).then(() => {
return res.json({
code: 200
});
... ...
'use strict';
const url = require('url');
const cache = global.yoho.cache.master;
const Promise = require('bluebird');
const co = Promise.coroutine;
... ... @@ -17,9 +18,15 @@ const index = co(function* (channel) {
};
});
const removeBlack = (remoteIp, apiLimitValidate) => {
const removeBlack = (remoteIp, apiLimitValidate, referer) => {
let operations = [];
if (referer) {
let pid = _.get(url.parse(referer, true), 'query.pid');
pid && operations.push(cache.delAsync(`${pid}:${remoteIp}`));
}
operations.push(cache.delAsync(`${config.app}:limiter:${remoteIp}`));
// 验证码之后一小时之内不再限制qps
... ...
/**
* 控制路由请求次数
* @date: 2018/03/05
*/
'use strict';
const _ = require('lodash');
const cache = global.yoho.cache.master;
const helpers = global.yoho.helpers;
const pathToRegexp = require('path-to-regexp');
const logger = global.yoho.logger;
const statusCode = {
code: 4403,
data: {},
message: '亲,您的访问次数过多,请稍后再试哦...'
};
const INVALIDTIME = 3600 * 24; // 24h
const IP_WHITE_LIST = [
'106.38.38.146',
'106.38.38.147',
'106.39.86.227',
'218.94.75.58',
'218.94.75.50',
'218.94.77.166'
];
const _jumpUrl = (req, res, next, result) => {
if (result.code === 4403) {
if (req.xhr) {
res.set({
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expires: (new Date(1900, 0, 1, 0, 0, 0, 0)).toUTCString()
});
return res.status(403).json(result);
}
return res.redirect(`${result.data.url}&refer=${req.originalUrl}`);
}
return next();
};
module.exports = () => {
return (req, res, next) => {
// default open
if (_.get(req.app.locals.pc, 'close.risk', false)) {
return next();
}
let ip = _.get(req.yoho, 'clientIp', '');
let path = req.path || '';
let risks = _.get(req.app.locals.pc, 'json.risk', []);
let router = {};
risks = [{
route: '/kids-brands/',
requests: 5
}];
logger.debug(`risk => risks: ${JSON.stringify(risks)}, path: ${path}, ip: ${ip}`); // eslint-disable-line
if (_.isEmpty(path) || _.isEmpty(risks) || IP_WHITE_LIST.indexOf(ip) > -1) {
return next();
}
_.isArray(risks) && risks.some(item => {
if (item.state === 'off') {
return false;
}
if (!item.regRoute) {
item.regRoute = pathToRegexp(item.route);
item.interval = parseInt(item.interval, 10);
item.requests = parseInt(item.requests, 10);
}
if (item.regRoute.test(path)) {
router = item;
return true;
}
return false;
});
logger.debug(`risk => router: ${JSON.stringify(router)}, path: ${path}`); // eslint-disable-line
if (_.isEmpty(router)) {
return next();
}
let keyPath = `${_.trim(path, '/').replace(/\//g, ':')}`;
let limitKey = `pc:risk:limit:${keyPath}:${ip}`;
let configKey = `pc:risk:${keyPath}:${ip}`;
let checkUrl = helpers.urlFormat('/3party/check', {
pid: `pc:risk:limit:${keyPath}`
});
return Promise.all([
cache.getAsync(limitKey),
cache.getAsync(configKey),
]).then(inters => {
logger.debug(`risk => getCache: ${JSON.stringify(inters)}, path: ${path}`); // eslint-disable-line
if (inters[0]) {
return Object.assign({}, statusCode, {data: {url: checkUrl}});
}
if (typeof inters[1] === 'undefined') {
cache.setAsync(configKey, 1, router.interval || 300);
return {code: 200};
}
inters[1] = parseInt(`0${inters[1]}`, 10);
if (inters[1] <= router.requests) {
router = [];
cache.incrAsync(configKey, 1);
return {code: 200};
}
return Promise.all([
cache.setAsync(limitKey, 1, INVALIDTIME),
cache.delAsync(configKey)
]).then(() => {
return Object.assign({}, statusCode, {data: {url: checkUrl}});
});
}).then(result => {
logger.debug(`risk => result: ${JSON.stringify(result)}, path: ${path}`); // eslint-disable-line
return _jumpUrl(req, res, next, result);
}).catch(e => {
console.log(`risk => path: ${path}, err: ${e.message}`);
return next();
});
};
};
... ...
... ... @@ -48,6 +48,7 @@
"passport-sina": "^0.1.0",
"passport-strategy": "1.x.x",
"passport-weixin": "^0.1.0",
"path-to-regexp": "^2.2.0",
"redis": "^2.7.1",
"request": "^2.81.0",
"request-ip": "^1.2.2",
... ...
... ... @@ -5452,6 +5452,10 @@ path-to-regexp@0.1.7:
version "0.1.7"
resolved "http://npm.yohops.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
path-to-regexp@^2.2.0:
version "2.2.0"
resolved "http://npm.yohops.com/path-to-regexp/-/path-to-regexp-2.2.0.tgz#80f0ff45c1e0e641da74df313644eaf115050972"
path-type@^1.0.0:
version "1.1.0"
resolved "http://npm.yohops.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
... ...