8

我想AntiForgeryTokens在每个 HttpPost 操作上使用一个 ActionFilter,该控制器位于一个名为ControllerBase该控制器的控制器中,每个其他控制器都继承自该控制器。

我想通过创建一个继承自它的 ActionFilter 来做到这一点,ValidateAntiForgeryToken它接受一个参数来告诉它要将自身应用于哪些 HTTP 动词。然后我想应用该过滤器 ControllerBase以确保AntiForgeryToken检查整个站点上的每个 POST 操作。

我正在考虑使用这个解决方案,但是

  • AuthorizationContext Constructor (ControllerContext)是一个过时的构造函数,我不确定如何使用推荐的AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor).

  • 默认情况下,它似乎没有使用 AntiForgeryToken,因为我收到以下错误:A required anti-forgery token was not supplied or was invalid在每次发布操作之后。

[HttpPost]我应该如何重写我的 ActionFilter 以满足当前的非过时标准并在每个动词上正确使用防伪标记?

我自己是否必须在每种形式中都包含防伪令牌(我想我会这样做)?(而不是自动生成 - 不要笑,我很好奇) 更新:正如评论中指出的那样;是的,这必须对每个表格进行。

这是我的 ControllerBase 中的代码供参考:

[UseAntiForgeryTokenOnPostByDefault]
public class ControllerBase : Controller 
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class BypassAntiForgeryTokenAttribute : ActionFilterAttribute
    {
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class UseAntiForgeryTokenOnPostByDefault : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (ShouldValidateAntiForgeryTokenManually(filterContext))
            {
                var authorizationContext = new AuthorizationContext(filterContext.Controller.ControllerContext);

                //Use the authorization of the anti forgery token, 
                //which can't be inhereted from because it is sealed
                new ValidateAntiForgeryTokenAttribute().OnAuthorization(authorizationContext);
            }

            base.OnActionExecuting(filterContext);
        }

        /// <summary>
        /// We should validate the anti forgery token manually if the following criteria are met:
        /// 1. The http method must be POST
        /// 2. There is not an existing [ValidateAntiForgeryToken] attribute on the action
        /// 3. There is no [BypassAntiForgeryToken] attribute on the action
        /// </summary>
        private static bool ShouldValidateAntiForgeryTokenManually(ActionExecutingContext filterContext)
        {
            var httpMethod = filterContext.HttpContext.Request.HttpMethod;

            //1. The http method must be POST
            if (httpMethod != "POST") return false;

            // 2. There is not an existing anti forgery token attribute on the action
            var antiForgeryAttributes =
                filterContext.ActionDescriptor.GetCustomAttributes(typeof (ValidateAntiForgeryTokenAttribute), false);

            if (antiForgeryAttributes.Length > 0) return false;

            // 3. There is no [BypassAntiForgeryToken] attribute on the action
            var ignoreAntiForgeryAttributes =
                filterContext.ActionDescriptor.GetCustomAttributes(typeof (BypassAntiForgeryTokenAttribute), false);

            if (ignoreAntiForgeryAttributes.Length > 0) return false;

            return true;
        }
    }
}
4

2 回答 2

7

我使用了以下方法:

public class SkipCSRFCheckAttribute : Attribute
{
}

public class AntiForgeryTokenFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (IsHttpPostRequest(filterContext) && !SkipCsrfCheck(filterContext))
            AntiForgery.Validate();
    }

    private static bool IsHttpPostRequest(AuthorizationContext filterContext)
    {
        return filterContext.RequestContext.HttpContext.Request.HttpMethod == HttpMethod.Post.ToString();
    }

    private static bool SkipCsrfCheck(AuthorizationContext filterContext)
    {
        return filterContext.ActionDescriptor.GetCustomAttributes(typeof (SkipCSRFCheck), false).Any();
    }
}

这使我们能够使用 SkipCSRFCheck 属性逐个禁用它,然后在 Application_Start 中将其注册为全局过滤器:

GlobalFilters.Filters.Add(new AntiForgeryTokenFilter());

于 2014-02-22T13:33:40.340 回答
1

您不需要实例化任何方法AuthorizationContext或调用该OnAuthorization方法,只需:

if (ShouldValidateAntiForgeryTokenManually(filterContext))
{
    AntiForgery.Validate(filterContext.HttpContext, null);
}
于 2012-08-03T06:30:54.340 回答