使用动作过滤器来限制每个用户对动作的访问次数

本文关键字:用户 访问 过滤器 | 更新日期: 2023-09-27 18:15:30

假设我们有一个动作,我们想要限制每个用户在一段时间内的访问次数,例如用户'A'不能在5分钟内访问样本动作超过10次,我对通过动作过滤器实现它很感兴趣有人知道吗?

使用动作过滤器来限制每个用户对动作的访问次数

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

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();
}

你可以把"Count"放到一个共享的地方,比如Session。

OnActionExecuting中这样做,并且考虑到并发问题,您需要在增加访问计数之前锁定会话,并尽快释放会话。

如果用户'A'满足访问限制,那么你可以通过设置

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

我最近做了一件事,它限制了用户在给定时间范围内访问url的次数,我采用了将条目粘贴到httpcontext缓存中的方法。

你可以在http://www.jambr.co.uk/Article/action-filter-request-throttle上看到我的博客文章。

这不是你需要的,但可以很容易地修改,而不是在缓存中存储一个空对象,你可以存储你的计数,然后你也可以存储一个滑动过期,而不是一个固定的?有问题就问吧