qps-limit.js 1.96 KB
/**
 * 限制页面访问次数,如超过限制次数,返回相应策略(目前是ip加入黑名单,跳转图形验证码页面,解除限制)
 * 当前规则只针对未登录用户
 */

'use strict';

const logger = global.yoho.logger;
const cache = global.yoho.cache.master;
const config = global.yoho.config;
const _ = require('lodash');

// 页面访问限制
const MAX_TIMES = {
    // 30s 最多访问15次
    30: 15,

    // 60s 最多访问15次
    60: 20,

    // 100s 最多访问15次
    600: 100
};

module.exports = (limiter, policy) => {
    const req = limiter.req;

    // 登录用户跳过
    if (!_.isEmpty(req.user)) {
        return Promise.resolve(true);
    }

    // 存储规则的cache keys
    let ruleKeys = [];

    _.forEach(MAX_TIMES, (val, key) => {
        ruleKeys.push(`${config.app}:limiter:${key}:max:${limiter.remoteIp}`); // eslint-disable-line
    });

    return cache.getMultiAsync(ruleKeys).then((results) => {

        // 第一次访问
        if (_.isEmpty(results)) {
            _.forEach(ruleKeys, (val) => {
                let cacheTime = val.match(/limiter:([^:]*)?:max/i)[1];

                cache.setAsync(val, 1, +cacheTime); // eslint-disable-line
            });

            return Promise.resolve(true);
        }

        // 遍历限制规则,若满足返回相应处理策略, 否则页面访问次数加1
        _.forEach(ruleKeys, (val) => {
            let cacheTime = +val.match(/limiter:([^:]*)?:max/i)[1];

            if (!results[val]) {
                cache.setAsync(val, 1, +cacheTime);
            } else if (+results[val] > +MAX_TIMES[cacheTime]) {
                return Promise.resolve(policy);
            }

            // 非异步请求访问记录加1
            if (!req.xhr) {
                cache.incrAsync(val, 1);
            }
        });

        // 不满足任何限制规则,继续访问
        return Promise.resolve(true);
    }).catch(err=>{
        logger.error(err);
    });
};