使用“操作过滤器”来限制每个用户的操作访问次数

时间:2011-11-24 05:53:43

标签: c# asp.net .net asp.net-mvc

假设我们有一个动作,我们希望限制每个用户在一段时间内的访问次数,例如用户'A'在5分钟内无法访问样本动作超过10次,我很有趣通过Action过滤器实现它有没有人知道它?

3 个答案:

答案 0 :(得分:4)

您可以编写自定义授权过滤器,该过滤器将存储每个用户在缓存中的给定操作的调用次数。通过配置缓存过期策略,该值将在该期限到期后自动逐出。

public class AuthorizeWithThrottleAttribute : AuthorizeAttribute
{
    private class Attempts
    {
        public int NumberOfAccess { get; set; }
    }

    public int Seconds { get; private set; }
    public int Count { get; private set; }

    public AuthorizeWithThrottleAttribute(int seconds, int count)
    {
        Seconds = seconds;
        Count = count;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        var rd = httpContext.Request.RequestContext.RouteData;
        var action = rd.GetRequiredString("action");
        var controller = rd.GetRequiredString("controller");
        // Remark: if you are using areas maybe you could also want
        // to constrain the key per area

        var key = string.Format("throttle-{0}-{1}-{2}", httpContext.User.Identity.Name, controller, action);
        var policy = new CacheItemPolicy
        {
            AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(Seconds),
        };

        // Here we are using the new caching API introduced in .NET 4.0:
        // http://msdn.microsoft.com/en-us/library/system.runtime.caching.aspx
        // If you are using an older version of the framework you could use
        // the legacy HttpContext.Cache instead which offers the same expiration
        // policy features as the new
        var attempts = MemoryCache.Default.Get(key) as Attempts;
        if (attempts == null)
        {
            attempts = new Attempts();
            MemoryCache.Default.Set(key, attempts, policy);
        }

        if (attempts.NumberOfAccess < Count)
        {
            attempts.NumberOfAccess++;
            return true;
        }
        httpContext.Items["throttled"] = true;
        return false;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var throttled = filterContext.HttpContext.Items["throttled"];
        if (throttled != null && (bool)throttled)
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
            filterContext.Result = new ContentResult
            {
                Content = string.Format("You may only call this action {0} times every {1} seconds", Count, Seconds),
                ContentType = "text/plain"
            };
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

然后装饰:

[AuthorizeWithThrottle(10, 5)]
public ActionResult Foo()
{
    return View();
}

答案 1 :(得分:0)

您可以将“计数”放入共享位置,例如会话。

OnActionExecuting中执行此操作,并考虑并发问题,您需要在阻止访问计数之前锁定会话,并尽快释放它。

如果用户'A'符合访问限制,则可以通过设置

进行重定向
filterContext.Result = new RedirectToRouteResult("route name", "route parameters")

答案 2 :(得分:0)

我最近做了一些事情,限制了用户在给定时间范围内访问网址的次数,我采取了将该项目粘贴到httpcontext缓存中的方法。

您可以在http://www.jambr.co.uk/Article/action-filter-request-throttle

上看到我的博文

这不是您需要的,但可以轻松修改,而不是在缓存中存储空对象,您可以存储您的计数,然后您还可以存储滑动到期而不是固定的?有问题请问。