Authored by yyq

limit

@@ -4,7 +4,9 @@ const _ = require('lodash'); @@ -4,7 +4,9 @@ const _ = require('lodash');
4 const logger = global.yoho.logger; 4 const logger = global.yoho.logger;
5 const ip = require('./rules/ip-list'); 5 const ip = require('./rules/ip-list');
6 const userAgent = require('./rules/useragent'); 6 const userAgent = require('./rules/useragent');
  7 +const ipWhiteList = require('./rules/ip-white-list');
7 const qpsLimiter = require('./rules/qps-limit'); 8 const qpsLimiter = require('./rules/qps-limit');
  9 +const co = Promise.coroutine;
8 10
9 // const asynchronous = require('./rules/asynchronous'); 11 // const asynchronous = require('./rules/asynchronous');
10 // const fakerLimiter = require('./rules/faker-limit'); 12 // const fakerLimiter = require('./rules/faker-limit');
@@ -13,12 +15,23 @@ const captchaPolicy = require('./policies/captcha'); @@ -13,12 +15,23 @@ const captchaPolicy = require('./policies/captcha');
13 // const reporterPolicy = require('./policies/reporter'); 15 // const reporterPolicy = require('./policies/reporter');
14 16
15 const IP_WHITE_LIST = [ 17 const IP_WHITE_LIST = [
16 - '106.38.38.146',  
17 - '106.38.38.147',  
18 - '106.39.86.227',  
19 - '218.94.75.58',  
20 - '218.94.75.50',  
21 - '218.94.77.166' 18 + '106.38.38.146', // 北京办公区域
  19 + '106.38.38.147', // 北京办公区域
  20 + '106.39.86.227', // 北京办公区域
  21 + '218.94.75.58', // 南京办公区域
  22 + '218.94.75.50', // 南京办公区域
  23 + '218.94.77.166', // 南京办公区域
  24 +
  25 + // '222.73.196.18', // B站合作方单击次数快加白名单
  26 + '123.206.73.107', // 腾讯云出口IP
  27 + '139.199.35.21', // 腾讯云出口IP
  28 + '139.199.29.44', // 腾讯云出口IP
  29 + '123.206.21.19' // 腾讯云出口IP
  30 +];
  31 +
  32 +const IP_WHITE_SEGMENT = [
  33 + '10.66.', // 内网IP段
  34 + '192.168.' // 内网IP段
22 ]; 35 ];
23 36
24 const PATH_WHITE_LIST = [ 37 const PATH_WHITE_LIST = [
@@ -36,30 +49,45 @@ const limiter = (rule, policy, context) => { @@ -36,30 +49,45 @@ const limiter = (rule, policy, context) => {
36 return rule(context, policy); 49 return rule(context, policy);
37 }; 50 };
38 51
39 -module.exports = (req, res, next) => {  
40 - let remoteIp = req.get('X-Yoho-Real-IP') || req.get('X-Forwarded-For') || req.get('X-Real-IP') || '';  
41 -  
42 - if (remoteIp.indexOf(',') > 0) {  
43 - let arr = remoteIp.split(',');  
44 -  
45 - remoteIp = arr[arr.length - 1];  
46 - } 52 +// 排除条件:ip白名单/路径白名单/异步请求/登录用户
  53 +const _excluded = (req) => {
  54 + let remoteIp = req.yoho.clientIp || '';
  55 + let remoteIpSegment = `${remoteIp.split('.').slice(0, 2).join('.')}.`;
  56 +
  57 + return co(function* () {
  58 + let atWhiteList = yield ipWhiteList(remoteIp);
  59 +
  60 + return Boolean(
  61 + atWhiteList ||
  62 + _.includes(IP_WHITE_LIST, remoteIp) ||
  63 + _.includes(IP_WHITE_SEGMENT, remoteIpSegment) ||
  64 + _.includes(PATH_WHITE_LIST, req.path) ||
  65 + req.xhr ||
  66 + !_.isEmpty(_.get(req, 'user.uid'))
  67 + );
  68 + })();
  69 +};
47 70
48 - remoteIp = _.trim(remoteIp); 71 +module.exports = (req, res, next) => {
  72 + const remoteIp = req.yoho.clientIp || '';
  73 + const enabled = !_.get(req.app.locals, 'pc.sys.noLimiter');
49 74
50 - if (_.startsWith(remoteIp, '10.66.')) {  
51 - remoteIp = req.get('X-Real-IP'); 75 + // 开关为关或者未获取到remoteIp,放行
  76 + if (!enabled || !remoteIp) {
  77 + logger.debug(`request remote ip: ${remoteIp}; enabled: ${enabled}`);
  78 + return next();
52 } 79 }
53 80
54 - // 排除条件:ip白名单/路径白名单/异步请求/登录用户  
55 - const excluded = _.includes(IP_WHITE_LIST, remoteIp) ||  
56 - _.includes(PATH_WHITE_LIST, req.path) || req.xhr || !_.isEmpty(_.get(req, 'user.uid'));  
57 - const enabled = !_.get(req.app.locals, 'pc.sys.noLimiter'); 81 + return co(function* () {
  82 + let excluded = yield _excluded(req);
58 83
59 logger.debug(`request remote ip: ${remoteIp}; excluded: ${excluded}; enabled: ${enabled}`); 84 logger.debug(`request remote ip: ${remoteIp}; excluded: ${excluded}; enabled: ${enabled}`);
60 85
61 - // 判断获取remoteIp成功,并且开关未关闭  
62 - if (enabled && remoteIp && !excluded) { 86 + // 白名单,放行
  87 + if (excluded) {
  88 + return next();
  89 + }
  90 +
63 const context = { 91 const context = {
64 req: req, 92 req: req,
65 res: res, 93 res: res,
@@ -67,14 +95,16 @@ module.exports = (req, res, next) => { @@ -67,14 +95,16 @@ module.exports = (req, res, next) => {
67 remoteIp: remoteIp 95 remoteIp: remoteIp
68 }; 96 };
69 97
70 - Promise.all([ 98 +
  99 + let results = yield Promise.all([
71 limiter(userAgent, captchaPolicy, context), 100 limiter(userAgent, captchaPolicy, context),
72 limiter(ip, captchaPolicy, context), 101 limiter(ip, captchaPolicy, context),
73 limiter(qpsLimiter, captchaPolicy, context) 102 limiter(qpsLimiter, captchaPolicy, context)
74 103
75 // limiter(asynchronous, captchaPolicy, context) 104 // limiter(asynchronous, captchaPolicy, context)
76 // limiter(fakerLimiter, reporterPolicy, context) 105 // limiter(fakerLimiter, reporterPolicy, context)
77 - ]).then((results) => { 106 + ]);
  107 +
78 let allPass = true, exclusion = false, policy = null; 108 let allPass = true, exclusion = false, policy = null;
79 109
80 logger.debug('limiter result: ' + JSON.stringify(results)); 110 logger.debug('limiter result: ' + JSON.stringify(results));
@@ -97,12 +127,8 @@ module.exports = (req, res, next) => { @@ -97,12 +127,8 @@ module.exports = (req, res, next) => {
97 } else { 127 } else {
98 return next(); 128 return next();
99 } 129 }
100 -  
101 - }).catch((err) => { 130 + })().catch((err) => {
102 logger.error(err); 131 logger.error(err);
103 return next(); 132 return next();
104 }); 133 });
105 - } else {  
106 - return next();  
107 - }  
108 }; 134 };
  1 +const co = Promise.coroutine;
  2 +const logger = global.yoho.logger;
  3 +const cache = global.yoho.cache.master;
  4 +const WHITE_LIST_KEY = 'whitelist:ip:';
  5 +
  6 +module.exports = (remoteIp) => {
  7 + let key = `${WHITE_LIST_KEY}${remoteIp}`;
  8 +
  9 + return co(function* () {
  10 + let result = Boolean(yield cache.getAsync(key));
  11 +
  12 + logger.debug(key, result);
  13 +
  14 + return result;
  15 + })();
  16 +};
@@ -15,8 +15,23 @@ module.exports = (limiter, policy) => { @@ -15,8 +15,23 @@ module.exports = (limiter, policy) => {
15 cache.getAsync(blackKey), 15 cache.getAsync(blackKey),
16 cache.getAsync(whiteKey) 16 cache.getAsync(whiteKey)
17 ]).then((args) => { 17 ]).then((args) => {
18 - const blacklist = args[0] || [],  
19 - whitelist = args[1] || []; 18 + let blacklist = [];
  19 + let whitelist = [];
  20 +
  21 + try {
  22 + blacklist = JSON.parse(args[0]);
  23 + } catch (error) {
  24 + logger.error(error);
  25 + }
  26 +
  27 + try {
  28 + whitelist = JSON.parse(args[1]);
  29 + } catch (error) {
  30 + logger.error(error);
  31 + }
  32 +
  33 + blacklist = blacklist || [];
  34 + whitelist = whitelist || [];
20 35
21 if (blacklist.length === 0 && whitelist.length === 0) { 36 if (blacklist.length === 0 && whitelist.length === 0) {
22 return Promise.resolve(true); 37 return Promise.resolve(true);