最近写的项目中涉及到防止灌水的功能,于是设计了黑名单中间件,跟大家分享一下,同时也希望大家有好的建议能够拍砖.

黑名单Schema:


/**
 * Created by YCXJ-wanglihui on 2014/5/28.
 */
'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

//1.短暂屏蔽 2.永久屏蔽
var degree = {TEMP:1, FOREVER:2};

/**
 * 黑名单
 * @type {Schema}
 *
 * @param ip {String} 黑名单Ip
 * @param createAt {Date} 创建时间
 * @param expireTime {Date} 如果是短暂屏蔽,屏蔽到期时间
 * @param forbiddenDegree {Number} 屏蔽级别 1.短暂屏蔽 2.永久屏蔽
 * @param reason {String} 屏蔽原因
 */
var BlackList = new Schema({
  ip:{
  type: String,
  index:true
  },
  createAt:{
  type: Date,
  default: Date.now
  },
  expireTime:{
  type: Date
  },
  forbiddenDegree:{
  type: Number,
  default:degree.TEMP
  },
  reason:{
  type: String,
  default: '请求次数频繁'
  }
});

mongoose.model('BlackList', BlackList);

IP与提交记录Schema:


/**
 * Created by YCXJ-wanglihui on 2014/5/28.
 */

'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

/**
 * 记录参与调查问卷的回复与Ip
 * @type {Schema}
 *
 * @param answerId {ObjectId} 回复Id
 * @param createAt {Date} 创建时间
 * @param ip {String} 参与回复的人Ip
 */
var IpAnswerLog = new Schema({
  answerId: {
  type: ObjectId
  },
  createAt: {
  type: Date,
  default:Date.now
  },
  ip:{
  type: String,
  index:true
  }
});

mongoose.model('IpAnswerLog', IpAnswerLog);

相关Proxy代码:


/**
 * Created by YCXJ-wanglihui on 2014/5/28.
 */
'use strict';

var IpAnswerLog = require('../models').IpAnswerLog;

/**
 * 新建并保存
 * @param ipAnswerLog {Schema or dict}
 * @param callback
 */
var newAndSave = function(ipAnswerLog, callback){
  if(ipAnswerLog instanceof IpAnswerLog){
  ipAnswerLog.save(callback);
  }else{
  var m = new IpAnswerLog(ipAnswerLog);
  m.save(callback);
  }
}

/**
 * 一分钟内回复数
 * @param ip
 * @param callback
 */
var countOneMinuteAnswer = function(ip, callback){
  var endTime = Date.now();
  var beginTime = endTime - 1000*60*1;
  countIpAnswerByTime(beginTime, endTime, ip, callback);
}

/**
 * 一小时内回复数字
 * @param ip
 * @param callback
 */
var countOneHourAnswer = function(ip, callback){
  var endTime = Date.now();
  var beginTime = endTime - 1000*60*60*1;
  countIpAnswerByTime(beginTime, endTime, ip, callback);
}

/**
 * 一天内回复
 * @param ip
 * @param callback
 */
var countOneDayAnswer = function(ip, callback){
  var endTime = Date.now();
  var beginTime = endTime - 1000*60*60*24;
  countIpAnswerByTime(beginTime, endTime, ip, callback);
}

/**
 * 计算某段时间内回复数
 * @param beginTime {Number} 开始时间 时间戳
 * @param endTime {Number} 结束时间 如果为null,使用当前时间 时间戳
 * @param ip  {String} Ip地址
 * @param callback
 */
var countIpAnswerByTime = function(beginTime, endTime, ip, callback){
  if(!endTime){
  endTime = Date.now();
  }
  IpAnswerLog.count({ip:ip, '$and':{$lt:beginTime, $gt:endTime}}, callback);
}

exports.countIpAnswerByTime =countIpAnswerByTime;
exports.countOneDayAnswer = countOneDayAnswer;
exports.countOneHourAnswer = countOneHourAnswer;
exports.countOneMinuteAnswer = countOneMinuteAnswer;
exports.newAndSave = newAndSave;

黑名单Proxy:


/**
 * Created by YCXJ-wanglihui on 2014/5/28.
 */
'use strict';
var BlackList = require('../models').BlackList;

/**
 * 新建并保存
 * @param backList {BlackList} or {dict} 黑名单数据
 * @param callback
 */
var newAndSave = function(backList, callback){
  if(backList instanceof BlackList){
  backList.save(callback);
  }else{
  var m = new BlackList(backList);
  m.save(callback);
  }
}

/**
 * 禁用Ip访问一小时
 * @param ip {String}
 * @param callback
 */
var newAndSaveOneHourTempForbidden = function(ip, callback){
  var expireTime = Date.now() + 1000*60*60;
  newAndSaveTempForbidden(ip,expireTime, callback);
}

/**
 * 禁用一天
 * @param ip {String}
 * @param callback
 */
var newAndSaveOneDayTempForbidden = function(ip, callback){
  var expireTime = Date.now() + 1000*60*60*24;
  newAndSaveTempForbidden(ip, expireTime, callback);
}

/**
 * 新建临时黑名单
 * @param ip {String}
 * @param expireTime {Number} 到期时间
 * @param callback
 */
var newAndSaveTempForbidden = function(ip, expireTime,callback){
  var blackList = new BlackList({ip:ip, expireTime:expireTime, forbiddenDegree:1});
  newAndSave(blackList, callback);
}

/**
 * 新建并保存永久黑名单
 * @param ip
 * @param callback
 */
var newAndSaveForeverForbidden = function(ip, callback){
  var blackList = new BlackList({ip:ip, forbiddenDegree:2});
  newAndSave(blackList, callback);
}

/**
 * 判断是否在黑名单中
 * @param ip {String} Ip地址
 * @param callback
 */
var isInBlackList = function(ip, callback){
  getBlackListByIp(ip, function(err, blackList){
  if(err){
  callback(err);
  }else if(blackList){
  var currentDate = Date.now();
  if(blackList.forbiddenDegree ===1 && blackList.expireTime> currentDate){
  removeBlackListByIp(ip, function(err){
  if(err){
  callback(err);
  }else{
  callback(null, false);
  }
  })
  }else{
  callback(null, true);
  }
  }else{
  callback(null, false);
  }
  })
}

/**
 * 通过Ip获取黑名单条目
 * @param ip
 * @param callback
 */
var getBlackListByIp = function(ip, callback){
  BlackList.findOne({ip:ip}, callback);
}

/**
 * 根据Ip删除黑名单
 * @param ip
 * @param callback
 */
var removeBlackListByIp = function(ip, callback){
  getBlackListByIp(ip, function(err, blackList){
  if(err){
  callback(err);
  }else if(blackList){
  blackList.remove(callback);
  }else{
  callback(null,null);
  }
  })
}

exports.newAndSave = newAndSave;
exports.isInBlackList = isInBlackList;
exports.getBlackListByIp = getBlackListByIp;
exports.removeBlackListByIp = removeBlackListByIp;
exports.newAndSaveOneHourTempForbidden = newAndSaveOneHourTempForbidden;
exports.newAndSaveOneDayTempForbidden = newAndSaveOneDayTempForbidden;
exports.newAndSaveForeverForbidden = newAndSaveForeverForbidden;
exports.newAndSaveTempForbidden = newAndSaveTempForbidden;

中间件详情:


/**
 * Created by YCXJ-wanglihui on 2014/5/28.
 */
'use strict';

var BlackListProxy = require('../../proxy').BlackListPorxy;
var IpAnswerLogProxy = require('../../proxy').IpAnswerLogProxy;
var EventProxy = require('eventproxy');

/**
 * 判断是否需要将Ip移动至黑名单中
 * @param req
 * @param res
 * @param next
 */
var isNeedMoveToBlackList = function(req, res, next){
  var ip = req.ip;
  //判断是否在黑名单中
  requireNotInBlackList(req, res, function(){
  var ep = new EventProxy();
  ep.fail(next);

  ep.all('minuteCount', 'hourCount', 'dayCount', function(minuteCount, hourCount, dayCount){
  if(minuteCount > 10){
  BlackListProxy.newAndSaveOneHourTempForbidden(ip, function(err, blackList){
  if(err){
  return next(err);
  }else{
  return res.send('提交过于频繁,1小时后重试!');
  }
  });
  }else if(hourCount > 100){
  BlackListProxy.newAndSaveOneDayTempForbidden(ip, function(err, blackList){
  if(err){
  return next(err);
  }else{
  return res.send('提交过于频繁,1天后重试!');
  }
  })
  }else if(dayCount > 1000){
  BlackListProxy.newAndSaveOneDayTempForbidden(ip, function(err, blackList){
  if(err){
  return next(err);
  }else{
  return res.send('提交过于频繁,1天后重试!');
  }
  })
  }else{
  return next();
  }
  })

  IpAnswerLogProxy.countOneMinuteAnswer(ip,ep.done('minuteCount'));
  IpAnswerLogProxy.countOneHourAnswer(ip, ep.done('hourCount'));
  IpAnswerLogProxy.countOneDayAnswer(ip, ep.done('dayCount'));
  });
}

/**
 * 中间件 要求Ip不在黑名单中
 * @param req
 * @param res
 * @param next
 */
var requireNotInBlackList = function(req, res, next){
  var ip = req.ip;
  BlackListProxy.isInBlackList(ip, function(err, result){
  if(err){
  next(err);
  }else if(result){
  return res.send('您的Ip禁止提交,如有疑问请联系lihui.wang@tulingdao.com');
  }else{
  next();
  }
  })
}

exports.isNeedMoveToBlackList = isNeedMoveToBlackList;
exports.requireNotInBlackList = requireNotInBlackList;

在路由中使用:


//网页提交接口
router.post('/create', middleware.isNeedMoveToBlackList, paperAnswers.create);

最新资讯
库克:公司对iPhone 12非常有信心

库克:公司对iPhone 12

苹果公司今天发布了2020财年第四财季业绩。报告显示,苹
苹果第四财季大中华区销售额79.5亿美元 同比大降28.5%

苹果第四财季大中华区

苹果公司今天发布了2020财年第四财季业绩。报告显示,苹
第四财季iPhone销量大幅下降 苹果股价盘后跌超4%

第四财季iPhone销量大

财报发布后, 苹果股价在截止目前的盘后交易中,大跌4.39%
苹果第四财季iPhone销量大降20.7%

苹果第四财季iPhone销

苹果公司今天发布了2020财年第四财季业绩。报告显示,苹
苹果第四财季营收647亿美元 净利同比降8%

苹果第四财季营收647

报告显示,苹果公司第四财季总净营收为646.98亿美元,比去
亚马逊第三季度营收961亿美元 净利同比激增197%

亚马逊第三季度营收96

亚马逊第三季度净利润为63.31亿美元,与去年同期的净利
最新文章
详解Vue的ref特性的使用

详解Vue的ref特性的使

这篇文章主要介绍了详解Vue的ref特性的使用,文中通过
vue学习笔记之slot插槽基本用法实例分析

vue学习笔记之slot插

这篇文章主要介绍了vue学习笔记之slot插槽基本用法,结
vue跳转方式(打开新页面)及传参操作示例

vue跳转方式(打开新页

这篇文章主要介绍了vue跳转方式(打开新页面)及传参操作,
vue学习笔记之过滤器的基本使用方法实例分析

vue学习笔记之过滤器

这篇文章主要介绍了vue学习笔记之过滤器的基本使用方
js获取本日、本周、本月的时间代码

js获取本日、本周、本

本篇文章给大家分享的内容是利用js如何获取本日、本周
node crawler如何添加promise支持

node crawler如何添加

这篇文章主要介绍了node crawler如何添加promise支持,