Authored by htoooth

fix add key

@@ -137,6 +137,10 @@ app.use((req, res, next) => { @@ -137,6 +137,10 @@ app.use((req, res, next) => {
137 next(); 137 next();
138 }); 138 });
139 139
  140 +function isOpenApmRisk(req) {
  141 + return _.get(req.app.locals, 'wap.open.apmrisk', false);
  142 +}
  143 +
140 // dispatcher 144 // dispatcher
141 try { 145 try {
142 const tdkUrl = require('./doraemon/middleware/redis-url'); 146 const tdkUrl = require('./doraemon/middleware/redis-url');
@@ -154,6 +158,8 @@ try { @@ -154,6 +158,8 @@ try {
154 const pageCache = require('./doraemon/middleware/page-cache'); 158 const pageCache = require('./doraemon/middleware/page-cache');
155 const downloadBar = require('./doraemon/middleware/download-bar'); 159 const downloadBar = require('./doraemon/middleware/download-bar');
156 const routeEncode = require('./doraemon/middleware/route-encode'); 160 const routeEncode = require('./doraemon/middleware/route-encode');
  161 + const ifElseMd = require('./doraemon/middleware/ifElseMd');
  162 + const riskManagementApm = require('./doraemon/middleware/risk-management2');
157 const riskManagement = require('./doraemon/middleware/risk-management'); 163 const riskManagement = require('./doraemon/middleware/risk-management');
158 const statics = require('./doraemon/middleware/statics'); 164 const statics = require('./doraemon/middleware/statics');
159 165
@@ -168,7 +174,10 @@ try { @@ -168,7 +174,10 @@ try {
168 174
169 // 请求限制中间件 175 // 请求限制中间件
170 if (!app.locals.devEnv) { 176 if (!app.locals.devEnv) {
171 - app.use(require('./doraemon/middleware/limiter')); 177 + const limiter = require('./doraemon/middleware/limiter');
  178 + const limiterApm = require('./doraemon/middleware/limiter/index2');
  179 +
  180 + app.use(ifElseMd(isOpenApmRisk, limiterApm, limiter));
172 } 181 }
173 182
174 if (app.locals.devEnv) { 183 if (app.locals.devEnv) {
@@ -181,7 +190,7 @@ try { @@ -181,7 +190,7 @@ try {
181 app.use(pageCache()); 190 app.use(pageCache());
182 app.use(routeEncode.md); 191 app.use(routeEncode.md);
183 app.use(downloadBar()); 192 app.use(downloadBar());
184 - app.use(riskManagement()); 193 + app.use(ifElseMd(isOpenApmRisk, riskManagementApm(), riskManagement()));
185 app.use(statics(app)); 194 app.use(statics(app));
186 195
187 require('./dispatch')(app); 196 require('./dispatch')(app);
@@ -44,6 +44,7 @@ exports.index = (req, res) => { @@ -44,6 +44,7 @@ exports.index = (req, res) => {
44 }); 44 });
45 }; 45 };
46 46
  47 +const limitKey = 'limit2';
47 48
48 const submitValidate = { 49 const submitValidate = {
49 errRes: { 50 errRes: {
@@ -54,6 +55,7 @@ const submitValidate = { @@ -54,6 +55,7 @@ const submitValidate = {
54 }, 55 },
55 clearLimitIp(req) { 56 clearLimitIp(req) {
56 let remoteIp = req.yoho.clientIp; 57 let remoteIp = req.yoho.clientIp;
  58 + let operations = [];
57 59
58 if (remoteIp.indexOf(',') > 0) { 60 if (remoteIp.indexOf(',') > 0) {
59 let arr = remoteIp.split(','); 61 let arr = remoteIp.split(',');
@@ -61,21 +63,51 @@ const submitValidate = { @@ -61,21 +63,51 @@ const submitValidate = {
61 remoteIp = arr[0]; 63 remoteIp = arr[0];
62 } 64 }
63 65
64 - let operations = [cache.delAsync(`${config.app}:limiter:${remoteIp}`)]; 66 + const isOpenApmrisk = _.get(req.app.locals, 'wap.open.apmrisk', false);
65 67
66 - // 验证码之后一小时之内不再限制qps  
67 - if (req.session.apiLimitValidate || req.session.apiRiskValidate) {  
68 - operations.push(cache.setAsync(  
69 - `${config.app}:limiter:api:ishuman:${remoteIp}`,  
70 - 1,  
71 - config.LIMITER_IP_TIME  
72 - )); 68 + // 新的计数
  69 + if (isOpenApmrisk) {
  70 + operations.push(cache.delAsync(`${config.app}:${limitKey}:${remoteIp}`));
  71 +
  72 + if (req.session.apiLimitValidate || req.session.apiRiskValidate) {
  73 + operations.push(cache.setAsync(
  74 + `${config.app}:limiter:api:ishuman:${remoteIp}`,
  75 + 1,
  76 + config.LIMITER_IP_TIME
  77 + ));
  78 + } else {
  79 + operations.push(cache.setAsync(
  80 + `${config.app}:${limitKey}:ishuman:${remoteIp}`,
  81 + 1,
  82 + config.LIMITER_IP_TIME
  83 + ));
  84 +
  85 + }
  86 +
  87 + _.forEach(config.REQUEST_LIMIT, (val, key) => {
  88 + operations.push(cache.delAsync(`${config.app}:${limitKey}:${key}:max:${remoteIp}`));
  89 + });
73 } else { 90 } else {
74 - operations.push(cache.setAsync(  
75 - `${config.app}:limiter:ishuman:${remoteIp}`,  
76 - 1,  
77 - config.LIMITER_IP_TIME  
78 - )); 91 + operations.push(cache.delAsync(`${config.app}:limiter:${remoteIp}`));
  92 +
  93 + // 验证码之后一小时之内不再限制qps
  94 + if (req.session.apiLimitValidate || req.session.apiRiskValidate) {
  95 + operations.push(cache.setAsync(
  96 + `${config.app}:limiter:api:ishuman:${remoteIp}`,
  97 + 1,
  98 + config.LIMITER_IP_TIME
  99 + ));
  100 + } else {
  101 + operations.push(cache.setAsync(
  102 + `${config.app}:limiter:ishuman:${remoteIp}`,
  103 + 1,
  104 + config.LIMITER_IP_TIME
  105 + ));
  106 + }
  107 +
  108 + _.forEach(config.REQUEST_LIMIT, (val, key) => {
  109 + operations.push(cache.delAsync(`${config.app}:limiter:${key}:max:${remoteIp}`));
  110 + });
79 } 111 }
80 112
81 delete req.session.apiLimitValidate; 113 delete req.session.apiLimitValidate;
@@ -87,10 +119,6 @@ const submitValidate = { @@ -87,10 +119,6 @@ const submitValidate = {
87 operations.push(cache.delAsync(riskPid)); 119 operations.push(cache.delAsync(riskPid));
88 } 120 }
89 121
90 - _.forEach(config.REQUEST_LIMIT, (val, key) => {  
91 - operations.push(cache.delAsync(`${config.app}:limiter:${key}:max:${remoteIp}`));  
92 - });  
93 -  
94 return Promise.all(operations); 122 return Promise.all(operations);
95 }, 123 },
96 geetest(req, res) { 124 geetest(req, res) {
@@ -135,7 +163,7 @@ const submitValidate = { @@ -135,7 +163,7 @@ const submitValidate = {
135 imgCheckRisk(req, res) { 163 imgCheckRisk(req, res) {
136 const self = this; 164 const self = this;
137 165
138 - co(function * () { 166 + co(function*() {
139 let result = yield req.ctx(checkModel).verifyImgCheckRisk(req.cookies.udid, req.body.captcha); 167 let result = yield req.ctx(checkModel).verifyImgCheckRisk(req.cookies.udid, req.body.captcha);
140 168
141 if (result.code === 200) { 169 if (result.code === 200) {
@@ -200,7 +200,11 @@ exports.serverError = () => { @@ -200,7 +200,11 @@ exports.serverError = () => {
200 } 200 }
201 201
202 if (!isHuman) { 202 if (!isHuman) {
203 - cache.setAsync(`${config.app}:limiter:${remoteIp}`, 1, config.LIMITER_IP_TIME); 203 + if (_.get(req.app.locals, 'wap.open.apmrisk', false)) {
  204 + cache.setAsync(`${config.app}:limit2:${remoteIp}`, 1, config.LIMITER_IP_TIME);
  205 + } else {
  206 + cache.setAsync(`${config.app}:limiter:${remoteIp}`, 1, config.LIMITER_IP_TIME);
  207 + }
204 208
205 req.session[sessionLimitKey] = true; 209 req.session[sessionLimitKey] = true;
206 210
  1 +
  2 +
  3 +module.exports = (predict, ifTrueFn, elseFn) => {
  4 + if (!ifTrueFn) {
  5 + ifTrueFn = (req, res, next) => next();
  6 + }
  7 +
  8 + if (!elseFn) {
  9 + elseFn = (req, res, next) => next();
  10 + }
  11 +
  12 + return (req, res, next) => {
  13 + if (predict(req, res)) {
  14 + ifTrueFn(req, res, next);
  15 + } else {
  16 + elseFn(req, res, next);
  17 + }
  18 + };
  19 +};
  1 +'use strict';
  2 +
  3 +const _ = require('lodash');
  4 +const logger = global.yoho.logger;
  5 +const ip = require('./rules/ip-list2');
  6 +const userAgent = require('./rules/useragent');
  7 +const ipWhiteList = require('./rules/ip-white-list');
  8 +const pathWhiteList = require('./rules/path-white-list');
  9 +const qpsLimiter = require('./rules/qps-limit2');
  10 +const co = Promise.coroutine;
  11 +
  12 +// const asynchronous = require('./rules/asynchronous');
  13 +// const fakerLimiter = require('./rules/faker-limit');
  14 +const captchaPolicy = require('./policies/captcha');
  15 +
  16 +// const reporterPolicy = require('./policies/reporter');
  17 +
  18 +const limiter = (rule, policy, context) => {
  19 + return rule(context, policy);
  20 +};
  21 +
  22 +// 排除条件:ip白名单/路径白名单/异步请求/登录用户
  23 +const _excluded = (req) => {
  24 + let remoteIp = req.yoho.clientIp || '';
  25 +
  26 + return co(function* () {
  27 + let atIPWhiteList = yield ipWhiteList(remoteIp);
  28 +
  29 + return Boolean(
  30 + atIPWhiteList ||
  31 + _.includes(pathWhiteList(), req.path) ||
  32 + req.xhr ||
  33 + !_.isEmpty(_.get(req, 'user.uid'))
  34 + );
  35 + })();
  36 +};
  37 +
  38 +module.exports = (req, res, next) => {
  39 + const remoteIp = req.yoho.clientIp || '';
  40 + const enabled = !_.get(req.app.locals, 'wap.sys.noLimiter');
  41 +
  42 + // 开关为关或者未获取到remoteIp,放行
  43 + if (!enabled || !remoteIp) {
  44 + logger.debug(`request remote ip: ${remoteIp}; enabled: ${enabled}`);
  45 + return next();
  46 + }
  47 +
  48 + co(function* () {
  49 + let excluded = yield _excluded(req);
  50 +
  51 + logger.debug(`request remote ip: ${remoteIp}; excluded: ${excluded}; enabled: ${enabled}`);
  52 +
  53 + // 白名单,放行
  54 + if (excluded) {
  55 + return next();
  56 + }
  57 + const context = {
  58 + req: req,
  59 + res: res,
  60 + next: next,
  61 + remoteIp: remoteIp
  62 + };
  63 +
  64 + let results = yield Promise.all([
  65 + limiter(userAgent, captchaPolicy, context),
  66 + limiter(ip, captchaPolicy, context),
  67 + limiter(qpsLimiter, captchaPolicy, context)
  68 +
  69 + // limiter(asynchronous, captchaPolicy, context)
  70 + // limiter(fakerLimiter, reporterPolicy, context)
  71 + ]);
  72 +
  73 + let allPass = true, exclusion = false, policy = null;
  74 +
  75 + logger.debug('limiter result: ' + JSON.stringify(results));
  76 +
  77 + _.forEach(results, (result) => {
  78 + if (typeof result === 'object' && !exclusion) {
  79 + exclusion = result.exclusion;
  80 + }
  81 +
  82 + if (typeof result === 'function') {
  83 + allPass = false;
  84 + policy = result;
  85 + }
  86 + });
  87 +
  88 + if (exclusion) {
  89 + return next();
  90 + } else if (!allPass && policy) {
  91 + policy(req, res, next);
  92 + } else {
  93 + return next();
  94 + }
  95 + })().catch(err => {
  96 + logger.error(err);
  97 + return next();
  98 + });
  99 +};
  1 +'use strict';
  2 +
  3 +const cache = global.yoho.cache.master;
  4 +const logger = global.yoho.logger;
  5 +const config = global.yoho.config;
  6 +
  7 +const limitKey = 'limit2';
  8 +
  9 +module.exports = (limiter, policy) => {
  10 + const ipBlackKey = `pc:limiter:${limiter.remoteIp}`; // ci ip黑名单
  11 + const ipLimitKey = `${config.app}:${limitKey}:${limiter.remoteIp}`; // 业务黑名单
  12 +
  13 + return Promise.all([
  14 + cache.getAsync(ipBlackKey),
  15 + cache.getAsync(ipLimitKey)
  16 + ]).then(result => {
  17 + let ipBlackRes = result[0];
  18 + let ipLimitRes = result[1];
  19 +
  20 + logger.debug(ipBlackKey, ipBlackRes);
  21 + logger.debug(ipLimitKey, ipLimitRes);
  22 +
  23 + if ((ipBlackRes && +ipBlackRes > 0) || (ipLimitRes && +ipLimitRes > 0)) {
  24 + return Promise.resolve(policy);
  25 + } else {
  26 + return Promise.resolve(true);
  27 + }
  28 + });
  29 +};
  1 +/**
  2 + * 限制页面访问次数,如超过限制次数,返回相应策略(目前是ip加入黑名单,跳转图形验证码页面,解除限制)
  3 + * 当前规则只针对未登录用户
  4 + */
  5 +
  6 +const logger = global.yoho.logger;
  7 +const cache = global.yoho.cache.master;
  8 +const config = global.yoho.config;
  9 +
  10 +const limitKey = 'limit2';
  11 +
  12 +module.exports = (limiter, policy) => {
  13 + let getOp = {};
  14 +
  15 + getOp.human = cache.getAsync(`${config.app}:${limitKey}:ishuman:${limiter.remoteIp}`);
  16 + getOp.limit = cache.getAsync(`${config.app}:${limitKey}:${limiter.remoteIp}`);
  17 +
  18 + return Promise.props(getOp).then((results) => {
  19 + console.log('enable ===>', results);
  20 + if (results.human) {
  21 + return Promise.resolve(true);
  22 + }
  23 +
  24 + if (results.limit) {
  25 + return Promise.resolve(policy);
  26 + }
  27 +
  28 + return Promise.resolve(true);
  29 + }).catch(err=>{
  30 + logger.error(err);
  31 +
  32 + return Promise.resolve(true);
  33 + });
  34 +};
  1 +/**
  2 + * 控制路由请求次数
  3 + * @date: 2018/03/05
  4 + */
  5 +'use strict';
  6 +
  7 +const _ = require('lodash');
  8 +const cache = global.yoho.cache.master;
  9 +const helpers = global.yoho.helpers;
  10 +const pathToRegexp = require('path-to-regexp');
  11 +const logger = global.yoho.logger;
  12 +const md5 = require('yoho-md5');
  13 +
  14 +const statusCode = {
  15 + code: 4403,
  16 + data: {},
  17 + message: '亲,您的访问次数过多,请稍后再试哦...'
  18 +};
  19 +
  20 +const IP_WHITE_LIST = [
  21 + '106.38.38.146',
  22 + '106.38.38.147',
  23 + '106.39.86.227',
  24 + '218.94.75.58',
  25 + '218.94.75.50',
  26 + '218.94.77.166'
  27 +];
  28 +
  29 +const _jumpUrl = (req, res, next, result) => {
  30 + if (result.code === 4403) {
  31 + if (req.xhr) {
  32 + res.set({
  33 + 'Cache-Control': 'no-cache',
  34 + Pragma: 'no-cache',
  35 + Expires: (new Date(1900, 0, 1, 0, 0, 0, 0)).toUTCString()
  36 + });
  37 + return res.status(403).json(result);
  38 + }
  39 + return res.redirect(`${result.data.url}&refer=${req.originalUrl}`);
  40 + }
  41 +
  42 + return next();
  43 +};
  44 +
  45 +const limitKey = 'limit2';
  46 +
  47 +module.exports = () => {
  48 + return (req, res, next) => {
  49 + // default open
  50 + if (_.get(req.app.locals.wap, 'close.risk', false)) {
  51 + return next();
  52 + }
  53 +
  54 + let ip = _.get(req.yoho, 'clientIp', '');
  55 + let path = req.path || '';
  56 + let risks = _.get(req.app.locals.wap, 'json.risk', []);
  57 + let router = {};
  58 +
  59 + logger.debug(`risk => risks: ${JSON.stringify(risks)}, path: ${path}, ip: ${ip}`); // eslint-disable-line
  60 + if (_.isEmpty(path) || _.isEmpty(risks) || IP_WHITE_LIST.indexOf(ip) > -1) {
  61 + return next();
  62 + }
  63 +
  64 + _.isArray(risks) && risks.some(item => {
  65 + if (item.state === 'off') {
  66 + return false;
  67 + }
  68 +
  69 + if (!item.regRoute) {
  70 + item.regRoute = pathToRegexp(item.route);
  71 + }
  72 +
  73 + if (item.regRoute.test(path)) {
  74 + router = item;
  75 + return true;
  76 + }
  77 +
  78 + return false;
  79 + });
  80 +
  81 + logger.debug(`risk => router: ${JSON.stringify(router)}, path: ${path}`); // eslint-disable-line
  82 + if (_.isEmpty(router)) {
  83 + return next();
  84 + }
  85 +
  86 + let keyPath = md5(`${router.regRoute}`);
  87 + let limitEnable = `wap:risk:${limitKey}:${keyPath}:${ip}`;
  88 + let checkUrl = helpers.urlFormat('/3party/check', {
  89 + pid: `wap:risk:${limitKey}:${keyPath}`
  90 + });
  91 +
  92 + cache.getAsync(limitEnable)
  93 + .then(result => {
  94 + logger.debug(`risk => getCache: ${JSON.stringify(result)}, path: ${path}`); // eslint-disable-line
  95 + if (result) {
  96 + return Object.assign({}, statusCode, {
  97 + data: {
  98 + url: checkUrl
  99 + }
  100 + });
  101 + } else {
  102 + return {
  103 + code: 200
  104 + };
  105 + }
  106 + }).then(result => {
  107 + logger.debug(`risk => result: ${JSON.stringify(result)}, path: ${path}`); // eslint-disable-line
  108 + return _jumpUrl(req, res, next, result);
  109 + }).catch(e => {
  110 + console.log(`risk => path: ${path}, err: ${e.message}`);
  111 + return next();
  112 + });
  113 + };
  114 +};
@@ -87,7 +87,7 @@ @@ -87,7 +87,7 @@
87 "xml2js": "^0.4.19", 87 "xml2js": "^0.4.19",
88 "yoho-express-session": "^2.0.0", 88 "yoho-express-session": "^2.0.0",
89 "yoho-md5": "^2.0.0", 89 "yoho-md5": "^2.0.0",
90 - "yoho-node-lib": "=0.6.26", 90 + "yoho-node-lib": "=0.6.28",
91 "yoho-zookeeper": "^1.0.10" 91 "yoho-zookeeper": "^1.0.10"
92 }, 92 },
93 "devDependencies": { 93 "devDependencies": {