web api 限制单个IP在一定时间内访问次数
ps:下面实例是每隔30秒访问次数不超过3次
1、Filter:
using Infrastructure.Log; using Infrastructure.Web; using Lemon.Stats.Model; using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Http.Filters; namespace Lemon.Stats.Apis { /// <summary> /// 限制单个IP短时间内访问次数 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class IPActionFilterAttribute : ActionFilterAttribute { /// <summary> /// 限制单个IP短时间内访问次数 /// </summary> /// <param name="actionContext"></param> public override void OnActionExecuting(HttpActionContext actionContext) { string ip = HttpHelper.GetClientIp(actionContext.Request); //var isValid = IPCacheHelper.CheckIsAble(ip); IPCacheInfoModel ipModel = IPCacheHelper.GetIPLimitInfo(ip); if (!ipModel.IsVisit) { Logger.Warn(string.Format("IP【{0}】被限制了【{1}】次数",ipModel.IP,ipModel.Limit)); actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "系统正忙,请稍微再试。"); return; } base.OnActionExecuting(actionContext); } } }
2、IPCacheHelper:
using Lemon.Stats.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Lemon.Stats.Apis { /// <summary> /// 限制单个IP访问次数 /// </summary> public class IPCacheHelper { /// <summary> /// IP缓存集合 /// </summary> private static List<IPCacheInfoModel> dataList = new List<IPCacheInfoModel>(); private static object lockObj = new object(); /// <summary> /// 一段时间内,最大请求次数,必须大于等于1 ///</summary> private static int maxTimes = 3; /// <summary> /// 一段时间长度(单位秒),必须大于等于1 /// </summary> private static int partSecond = 30; /// <summary> /// 请求被拒绝是否加入请求次数 /// </summary> private static bool isFailAddIn = false; static IPCacheHelper() { } /// <summary> /// 设置时间,默认maxTimes=3, partSecond=30 /// </summary> /// <param name="_maxTimes">最大请求次数</param> /// <param name="_partSecond">请求单位时间</param> public static void SetTime(int _maxTimes, int _partSecond) { maxTimes = _maxTimes; partSecond = _partSecond; } /// <summary> /// 检测一段时间内,IP的请求次数是否可以继续请求和使用 /// </summary> /// <param name="ip">ip</param> /// <returns></returns> public static bool CheckIsAble(string ip) { lock (lockObj) { var item = dataList.Find(p => p.IP == ip); if (item == null) { item = new IPCacheInfoModel(); item.IP = ip; item.ReqTime.Add(DateTime.Now); dataList.Add(item); return true; } else { if (item.ReqTime.Count > maxTimes) { item.ReqTime.RemoveAt(0); } var nowTime = DateTime.Now; if (isFailAddIn) { #region 请求被拒绝也需要加入当次请求 item.ReqTime.Add(nowTime); if (item.ReqTime.Count >= maxTimes) { if (item.ReqTime[0].AddSeconds(partSecond) > nowTime) { return false; } else { return true; } } else { return true; } #endregion } else { #region 请求被拒绝就不需要加入当次请求了 if (item.ReqTime.Count >= maxTimes) { if (item.ReqTime[0].AddSeconds(partSecond) > nowTime) { return false; } else { item.ReqTime.Add(nowTime); return true; } } else { item.ReqTime.Add(nowTime); return true; } #endregion } } } } /// <summary> /// 检测一段时间内,IP的请求次数是否可以继续请求和使用 /// </summary> /// <param name="ip">ip</param> /// <returns></returns> public static IPCacheInfoModel GetIPLimitInfo(string ip) { lock (lockObj) { var item = dataList.Find(p => p.IP == ip); if (item == null) //IP开始访问 { item = new IPCacheInfoModel(); item.IP = ip; item.ReqTime.Add(DateTime.Now); dataList.Add(item); item.IsVisit = true; //可以继续访问 return item; } else { if (item.ReqTime.Count > maxTimes) { item.ReqTime.RemoveAt(0); } var nowTime = DateTime.Now; if (isFailAddIn) { #region 请求被拒绝也需要加入当次请求 item.ReqTime.Add(nowTime); if (item.ReqTime.Count >= maxTimes) { if (item.ReqTime[0].AddSeconds(partSecond) > nowTime) { item.Limit++; //限制次数+1 item.IsVisit = false;//不能继续访问 return item; } else { item.IsVisit = true; //可以继续访问 return item; //单个IP30秒内 没有多次访问 } } else { item.IsVisit = true; //可以继续访问 return item; //单个IP访问次数没有达到max次数 } #endregion } else { #region 请求被拒绝就不需要加入当次请求了 if (item.ReqTime.Count >= maxTimes) { if (item.ReqTime[0].AddSeconds(partSecond) > nowTime) { item.Limit++; //限制次数+1 item.IsVisit = false;//不能继续访问 return item; } else { item.ReqTime.Add(nowTime); item.IsVisit = true; //可以继续访问 return item; } } else { item.ReqTime.Add(nowTime); item.IsVisit = true; //可以继续访问 return item; } #endregion } } } } } }
3、IPCacheInfoModel:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Lemon.Stats.Model { public class IPCacheInfoModel { /// <summary> /// IP /// </summary> public string IP { get; set; } /// <summary> /// 限制次数 /// </summary> public int Limit { get; set; } /// <summary> /// 是否可以访问 /// </summary> public bool IsVisit { get; set; } /// <summary> /// 访问时间 /// </summary> private List<DateTime> reqTime = new List<DateTime>(); /// <summary> /// 访问时间 /// </summary> public List<DateTime> ReqTime { get { return this.reqTime; } set { this.reqTime = value; } } } }
4、Action:
/// <summary> /// IP,PV(VV),UV,注册用户点击量统计 /// 先执行IPActionFilter过滤器,再执行ChannelActionFilter过滤器 /// 先执行后面的过滤器,再执行前面的过滤器,执行方式倒序执行顺序 /// </summary> [ChannelActionFilter, IPActionFilter, RoutePrefix("Stats")] public class StatsController : ApiController { /// <summary> /// 每次页面点击都统计数据,直接由客户端调用 /// Header中加入SecretKey,AppKey,UniqueKey /// </summary> /// <returns></returns> [HttpGet, Route("")] public async Task Get() { } }