Authored by 姜枫

copy from pc limiter

  1 +'use strict';
  2 +const limiter = require('../middleware/limiter/index');
  3 +
  4 +module.exports = (req, res, next) => {
  5 + return limiter(req, res, next);
  6 +};
  1 +'use strict';
  2 +
  3 +const _ = require('lodash');
  4 +const logger = global.yoho.logger;
  5 +const ip = require('./rules/ip-list');
  6 +const userAgent = require('./rules/useragent');
  7 +const qpsLimiter = require('./rules/qps-limit');
  8 +const fakerLimiter = require('./rules/faker-limit');
  9 +const captchaPolicy = require('./policies/captcha');
  10 +const reporterPolicy = require('./policies/reporter');
  11 +
  12 +const IP_WHITE_LIST = [
  13 + // '106.38.38.146',
  14 + // '218.94.75.58'
  15 +];
  16 +
  17 +const limiter = (rule, policy, context) => {
  18 + return rule(context, policy);
  19 +};
  20 +
  21 +module.exports = (req, res, next) => {
  22 + let remoteIp = req.get('X-Forwarded-For') || req.connection.remoteAddress;
  23 + logger.debug('request remote ip: ', remoteIp);
  24 +
  25 + if (remoteIp.indexOf(',') > 0) {
  26 + let arr = remoteIp.split(',');
  27 +
  28 + remoteIp = arr[0];
  29 + }
  30 +
  31 + const excluded = _.includes(IP_WHITE_LIST, remoteIp);
  32 + const enabled = !_.get(req.app.locals, 'wap.sys.noLimiter');
  33 +
  34 + // 判断获取remoteIp成功,并且开关未关闭
  35 + if (enabled && remoteIp && !excluded) {
  36 + const context = {
  37 + req: req,
  38 + res: res,
  39 + next: next,
  40 + remoteIp: remoteIp
  41 + };
  42 +
  43 + Promise.all([
  44 + limiter(userAgent, captchaPolicy, context),
  45 + limiter(ip, captchaPolicy, context),
  46 + limiter(qpsLimiter, captchaPolicy, context),
  47 + //limiter(fakerLimiter, reporterPolicy, context)
  48 + ]).then((results) => {
  49 + let allPass = true, exclusion = false, policy = null;
  50 +
  51 + logger.debug('limiter result: ' + JSON.stringify(results));
  52 +
  53 + _.forEach(results, (result) => {
  54 + if (typeof result === 'object' && !exclusion) {
  55 + exclusion = result.exclusion;
  56 + }
  57 +
  58 + if (!excluded && typeof result === 'function') {
  59 + allPass = false;
  60 + }
  61 +
  62 + if (typeof result === 'function') {
  63 + policy = result;
  64 + }
  65 + });
  66 +
  67 + if (exclusion) {
  68 + return next();
  69 + } else if (!allPass && policy) {
  70 + policy(req, res, next);
  71 + } else {
  72 + return next();
  73 + }
  74 +
  75 + }).catch((err) => {
  76 + logger.error(err);
  77 + return next();
  78 + });
  79 + } else {
  80 + return next();
  81 + }
  82 +};
  1 +'use strict';
  2 +
  3 +const helpers = global.yoho.helpers;
  4 +const _ = require('lodash');
  5 +
  6 +const WHITE_LIST = [
  7 + '/3party/check',
  8 + '/passport/imagesNode',
  9 + '/passport/cert/headerTip'
  10 +];
  11 +
  12 +module.exports = (req, res, next) => {
  13 + let refer = req.method === 'GET' ? req.get('Referer') : '';
  14 + let limitAPI = helpers.urlFormat('/3party/check', {refer: refer});
  15 + let limitPage = helpers.urlFormat('/3party/check', {refer: req.protocol + '://' + req.get('host') + req.originalUrl});
  16 +
  17 + if (_.indexOf(WHITE_LIST, req.path) >= 0) {
  18 + return next();
  19 + }
  20 +
  21 + if (req.xhr) {
  22 + return res.json({
  23 + code: 400,
  24 + data: {refer: limitAPI}
  25 + });
  26 + }
  27 +
  28 + return res.redirect(limitPage);
  29 +};
  1 +'use strict';
  2 +
  3 +module.exports = (req, res, next) => {
  4 + return next();
  5 +};
  1 +'use strict';
  2 +
  3 +const logger = global.yoho.logger;
  4 +const cache = global.yoho.cache.master;
  5 +const ONE_DAY = 60 * 60 * 24;
  6 +
  7 +module.exports = (limiter, policy) => {
  8 + const req = limiter.req,
  9 + res = limiter.res,
  10 + next = limiter.next;
  11 +
  12 + const key = `pc:limiter:faker:${limiter.remoteIp}`;
  13 +
  14 + if (req.header('X-Requested-With') === 'XMLHttpRequest') {
  15 + cache.decrAsync(key, 1);
  16 + }
  17 +
  18 + res.on('render', function() {
  19 + cache.incrAsync(key, 1);
  20 + });
  21 +
  22 + return cache.getAsync(key).then((result) => {
  23 + if (result) {
  24 + if (result > 100) {
  25 + return Promise.resolve(policy);//policy(req, res, next);
  26 + } else {
  27 + return Promise.resolve(true);
  28 + }
  29 + } else {
  30 + cache.setAsync(key, 1, ONE_DAY); // 设置key,1m失效
  31 + return Promise.resolve(true);
  32 + }
  33 + });
  34 +};
  1 +'use strict';
  2 +
  3 +const cache = global.yoho.cache.master;
  4 +const _ = require('lodash');
  5 +
  6 +module.exports = (limiter) => {
  7 + const key = `pc:limiter:${limiter.remoteIp}`;
  8 +
  9 + return cache.getAsync(key).then((result) => {
  10 + if (result && _.isNumber(result)) {
  11 + return Promise.resolve({
  12 + exclusion: result === -1
  13 + });
  14 + } else {
  15 + return Promise.resolve(true);
  16 + }
  17 + });
  18 +};
  1 +'use strict';
  2 +
  3 +const logger = global.yoho.logger;
  4 +const cache = global.yoho.cache.master;
  5 +const config = global.yoho.config;
  6 +const ONE_DAY = 60 * 60 * 24;
  7 +const MAX_QPS = config.maxQps;
  8 +const _ = require('lodash');
  9 +
  10 +const PAGES = {
  11 + '/product/\\/pro_([\\d]+)_([\\d]+)\\/(.*)/': 5,
  12 + '/product/list/index': 5
  13 +};
  14 +
  15 +function urlJoin(a, b) {
  16 + if (_.endsWith(a, '/') && _.startsWith(b, '/')) {
  17 + return a + b.substring(1, b.length);
  18 + } else if (!_.endsWith(a, '/') && !_.startsWith(b, '/')) {
  19 + return a + '/' + b;
  20 + } else {
  21 + return a + b;
  22 + }
  23 +}
  24 +
  25 +module.exports = (limiter, policy) => {
  26 + const req = limiter.req,
  27 + res = limiter.res,
  28 + next = limiter.next;
  29 +
  30 + const key = `pc:limiter:${limiter.remoteIp}`;
  31 +
  32 + res.on('render', function() {
  33 + let route = req.route ? req.route.path : '';
  34 + let appPath = req.app.mountpath;
  35 +
  36 + if (_.isArray(route) && route.length > 0) {
  37 + route = route[0];
  38 + }
  39 +
  40 + let pageKey = urlJoin(appPath, route.toString()); // route may be a regexp
  41 + let pageIncr = PAGES[pageKey] || 0;
  42 +
  43 + if (/^\/p([\d]+)/.test(req.path)) {
  44 + pageIncr = 5;
  45 + }
  46 +
  47 + if (pageIncr > 0) {
  48 + cache.incrAsync(key, pageIncr);
  49 + }
  50 + });
  51 +
  52 + return cache.getAsync(key).then((result) => {
  53 + logger.debug('qps limiter: ' + key + '@' + result + ' max: ' + MAX_QPS);
  54 +
  55 + if (result && _.isNumber(result)) {
  56 +
  57 + if (result === -1) {
  58 + return Promise.resolve(true);
  59 + }
  60 +
  61 + if (result > MAX_QPS) { // 判断 qps
  62 + cache.touch(key, ONE_DAY);
  63 + logger.debug('req limit', key);
  64 +
  65 + return Promise.resolve(policy);
  66 + } else {
  67 + cache.incrAsync(key, 1); // qps + 1
  68 + return Promise.resolve(true);
  69 +
  70 + }
  71 + } else {
  72 + cache.setAsync(key, 1, 60); // 设置key,1m失效
  73 + return Promise.resolve(true);
  74 + }
  75 + });
  76 +};
  1 +'use strict';
  2 +
  3 +const cache = global.yoho.cache.master;
  4 +const _ = require('lodash');
  5 +const logger = global.yoho.logger;
  6 +
  7 +
  8 +module.exports = (limiter, policy) => {
  9 + const req = limiter.req,
  10 + res = limiter.res,
  11 + next = limiter.next;
  12 + const blackKey = 'pc:limiter:ua:black',
  13 + whiteKey = 'pc:limiter:ua:white';
  14 +
  15 + const ua = limiter.req.header('User-Agent');
  16 +
  17 + return Promise.all([
  18 + cache.getAsync(blackKey),
  19 + cache.getAsync(whiteKey)
  20 + ]).then((args) => {
  21 + const blacklist = args[0] || [], whitelist = args[1] || [];
  22 +
  23 + if (blacklist.length === 0 && whitelist.length === 0) {
  24 + return Promise.resolve(true);
  25 + }
  26 +
  27 + const test = (list) => {
  28 + let result = false;
  29 +
  30 + _.each(list, (item) => {
  31 + let regexp;
  32 +
  33 + try {
  34 + regexp = new RegExp(item);
  35 + } catch (e) {
  36 + logger.error(e);
  37 + }
  38 +
  39 + if (regexp.test(ua)) {
  40 + result = true;
  41 + }
  42 + });
  43 +
  44 + return result;
  45 + };
  46 +
  47 + if (test(blacklist)) {
  48 + return Promise.resolve(policy);
  49 + } else if (test(whitelist)) {
  50 + return Promise.resolve({
  51 + exclusion: true
  52 + });
  53 + } else {
  54 + return Promise.resolve(true);
  55 + }
  56 + });
  57 +
  58 +};