Web防止请求过于频繁的一种方法

为了防止请求过于频繁或防止恶意循环暴力访问,我们需要对请求频率进行检测与控制,有效的检测与控制既能保证正常的访问不受影响,又能防止异常访问。


控制原理:记录每次(总次数设定上限,超过要求的频率即可)访问的时间,比较当前访问时间与向前指定次数那次的访问时间,如果时间短于规定的时长,则表示已经超过规定时长内访问次数了,即访问过于频繁了。


上代码:

public class FrequencyControler {
    /// <summary>
    /// 访问控制器名称,用于区分其它控制器,支持多个控制器
    /// </summary>
    private string Name { get; set; }

    /// <summary>
    /// 限定时长
    /// </summary>
    private int Seconds { get; set; }

    /// <summary>
    /// 限定次数
    /// </summary>
    private int Times { get; set; }
    public readonly int MAX_TIMES = 100;

    #region 私有方法
    private string SessionNameDatelist {
        get { return String.Format("fc.{0}.datelist", Name); }
    }

    private string SessionNameDatepos {
        get { return String.Format("fc.{0}.datepos", Name); }
    }

    /// <summary>
    /// 取得用于保存每次访问时间点的数组(做队列用)
    /// </summary>
    /// <returns></returns>
    private long[] GetDateList() {
        if (HttpContext.Current.Session[SessionNameDatelist] == null) {
            HttpContext.Current.Session[SessionNameDatelist] = new long[MAX_TIMES];
        }
        return (long[])HttpContext.Current.Session[SessionNameDatelist];
    }

    /// <summary>
    /// 获取时间记录位置,相当于当前队列位置
    /// </summary>
    /// <returns></returns>
    private int GetDatepos() {
        if (HttpContext.Current.Session[SessionNameDatepos] == null) {
            HttpContext.Current.Session[SessionNameDatepos] = 0;
        }
        return (int)HttpContext.Current.Session[SessionNameDatepos];
    }

    /// <summary>
    /// 设置时间记录位置,相当于当前队列位置
    /// </summary>
    /// <param name="pos"></param>
    private void SetDatepos(int pos) {
        HttpContext.Current.Session[SessionNameDatepos] = pos;
    }
    #endregion

    /// <summary>
    /// 构造访问检测器,限定指定时间内最多请求次数
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="seconds">限定时间范围(秒数)</param>
    /// <param name="times">限定次数</param>
    public FrequencyControler(string name, int seconds, int times) {
        Name = name;
        Seconds = seconds;
        Times = times;
        if (times > MAX_TIMES) throw new Exception("限定次数设置值超出上限!");
    }

    /// <summary>
    /// 记录一次访问,在时间点数组的下一个位置(按最大长度循环覆盖存储)
    /// </summary>
    public void Record() {
        long[] ds = GetDateList();
        int pos = GetDatepos();
        pos = (pos + 1) % ds.Length;
        ds[pos] = DateTime.Now.Ticks;
        SetDatepos(pos);
    }

    /// <summary>
    /// 判断是否达到访问限制(并默认记录一次请求)
    /// </summary>
    /// <returns></returns>
    public bool IsTooFrequently() {
        return IsTooFrequently(true);
    }

    /// <summary>
    /// 判断是否达到访问限制(主要外部使用功能)
    /// </summary>
    /// <param name="isRecord">是否包含本次请求,为true时,会先记录一次请求再判断</param>
    /// <returns></returns>
    public bool IsTooFrequently(bool isRecord) {
        if (isRecord) Record();

        long[] ds = GetDateList();
        int pos = GetDatepos();

        // 取得之前 限定次数 位置的时间点与最后的时间点比较,
        // 如果之前的访问次数还没有达到限定次数则忽略
        int pre_pos = (pos + ds.Length - Times + 1) % ds.Length;
        long pre_ticks = ds[pre_pos];
        long now_ticks = ds[pos];
        // 如果访问的次烤都还没有达到限定次数,pre_ticks 必定为0,所以大于0时才进行比较
        if (pre_ticks > 0) { 
            TimeSpan span = new TimeSpan(now_ticks - pre_ticks);
            if (span.TotalSeconds <= Seconds) {
                return true;
            }
        }
        return false;
    }
}


示例,在需要控制频率的操作按钮事件中:

    // 定义访问控制器允许10秒内3次请求
    public static FrequencyControler DoFrequency = new FrequencyControler("act", 10, 3);

    protected void Act(object sender, EventArgs e) {
        // 是否超过10秒内3次操作? 
        if (DoFrequency.IsTooFrequently(true)) {
            Console.Write("访问过于频繁!");
            return;
        }

        // do something...
    }

So easy! 在需要的地方,定义一个访问控制器即可轻松控制,也可以在一起控制访问频率的地方共用同一个访问控制器。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值