在ASP.NET MVC中实现请求限制的最佳方法?

时间:2020-03-05 18:44:50  来源:igfitidea点击:

我们正在尝试各种方法来限制给定时间段内的用户操作:

  • 限制问题/答案帖
  • 限制编辑
  • 限制提要检索

目前,我们正在使用缓存简单地插入用户活动的记录(如果该记录存​​在)(如果/当用户进行相同活动时),我们会进行限制。

自动使用缓存可以为我们提供陈旧的数据清理和滑动用户活动窗口,但是如何扩展可能是个问题。

还有什么其他方式可以确保可以有效地限制请求/用户操作(强调稳定性)?

解决方案

回答

我们使用从此URL http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx借用的技术,不是为了节流,而是为了穷人的拒绝服务(D.O.S)。这也是基于缓存的,可能与我们正在执行的操作类似。我们是否在节流以防D.O.S.攻击?当然可以使用路由器来减少D.O.S;我们认为路由器可以处理我们需要的节流吗?

回答

Microsoft具有IIS 7的新扩展,称为IIS 7.0 Beta的动态IP限制扩展。

"The Dynamic IP Restrictions for IIS 7.0 is a module that provides protection against denial of service and brute force attacks on web server and web sites. Such protection is provided by temporarily blocking IP addresses of the HTTP clients who make unusually high number of concurrent requests or who make large number of requests over small period of time."
  http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/

例子:

如果我们将条件设置为在"以X毫秒为单位的X请求"或者"以Y毫秒为单位的X个并发连接之后"阻止,则IP地址将被阻止"以Y毫秒为单位",然后将再次允许请求。

回答

这是过去一年中我们在Stack Overflow上一直使用的通用版本:

/// <summary>
/// Decorates any MVC route that needs to have client requests limited by time.
/// </summary>
/// <remarks>
/// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleAttribute : ActionFilterAttribute
{
    /// <summary>
    /// A unique name for this Throttle.
    /// </summary>
    /// <remarks>
    /// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
    /// </remarks>
    public string Name { get; set; }

    /// <summary>
    /// The number of seconds clients must wait before executing this decorated route again.
    /// </summary>
    public int Seconds { get; set; }

    /// <summary>
    /// A text message that will be sent to the client upon throttling.  You can include the token {n} to
    /// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
    /// </summary>
    public string Message { get; set; }

    public override void OnActionExecuting(ActionExecutingContext c)
    {
        var key = string.Concat(Name, "-", c.HttpContext.Request.UserHostAddress);
        var allowExecute = false;

        if (HttpRuntime.Cache[key] == null)
        {
            HttpRuntime.Cache.Add(key,
                true, // is this the smallest data we can have?
                null, // no dependencies
                DateTime.Now.AddSeconds(Seconds), // absolute expiration
                Cache.NoSlidingExpiration,
                CacheItemPriority.Low,
                null); // no callback

            allowExecute = true;
        }

        if (!allowExecute)
        {
            if (String.IsNullOrEmpty(Message))
                Message = "You may only perform this action every {n} seconds.";

            c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
            // see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
        }
    }
}

用法示例:

[Throttle(Name="TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)]
public ActionResult TestThrottle()
{
    return Content("TestThrottle executed");
}

通过使用它,ASP.NET缓存在这里像冠军一样工作,我们可以自动清除油门条目。随着流量的增长,我们看不到这是服务器上的问题。

随时提供有关此方法的反馈;当我们使Stack Overflow更好时,我们可以更快地获得Ewok的修复:)