index.js 2.94 KB
'use strict';

const _ = require('lodash');
const logger = global.yoho.logger;
const ip = require('./rules/ip-list');
const userAgent = require('./rules/useragent');
const qpsLimiter = require('./rules/qps-limit');

// const asynchronous = require('./rules/asynchronous');
// const fakerLimiter = require('./rules/faker-limit');
const captchaPolicy = require('./policies/captcha');

// const reporterPolicy = require('./policies/reporter');

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 PATH_WHITE_LIST = [
    '/3party/check',
    '/passport/images.png',
    '/passport/cert/headerTip',
    '/common/getbanner',
    '/common/suggestfeedback'
];

const limiter = (rule, policy, context) => {
    return rule(context, policy);
};

module.exports = (req, res, next) => {
    let remoteIp = req.get('X-Forwarded-For') || req.get('X-Real-IP') || '';

    if (remoteIp.indexOf(',') > 0) {
        let arr = remoteIp.split(',');

        remoteIp = arr[arr.length - 1];
    }

    remoteIp = _.trim(remoteIp);

    if (_.startsWith(remoteIp, '10.66.')) {
        remoteIp = req.get('X-Real-IP');
    }

    // 排除条件:ip白名单/路径白名单/异步请求/登录用户
    const excluded = _.includes(IP_WHITE_LIST, remoteIp) ||
        _.includes(PATH_WHITE_LIST, req.path) || req.xhr || !_.isEmpty(_.get(req, 'user.uid'));
    const enabled = !_.get(req.app.locals, 'pc.sys.noLimiter');

    logger.info(`request remote ip: ${remoteIp}; excluded: ${excluded}; enabled: ${enabled}`);

    // 判断获取remoteIp成功,并且开关未关闭
    if (enabled && remoteIp && !excluded) {
        const context = {
            req: req,
            res: res,
            next: next,
            remoteIp: remoteIp
        };

        Promise.all([
            limiter(userAgent, captchaPolicy, context),
            limiter(ip, captchaPolicy, context),
            limiter(qpsLimiter, captchaPolicy, context)

            // limiter(asynchronous, captchaPolicy, context)
            // limiter(fakerLimiter, reporterPolicy, context)
        ]).then((results) => {
            let allPass = true, exclusion = false, policy = null;

            logger.debug('limiter result: ' + JSON.stringify(results));

            _.forEach(results, (result) => {
                if (typeof result === 'object' && !exclusion) {
                    exclusion = result.exclusion;
                }

                if (typeof result === 'function') {
                    allPass = false;
                    policy = result;
                }
            });

            if (exclusion) {
                return next();
            } else if (!allPass && policy) {
                policy(req, res, next);
            } else {
                return next();
            }

        }).catch((err) => {
            logger.error(err);
            return next();
        });
    } else {
        return next();
    }
};