Asp.net-Mvc

我將如何編寫一個 ActionFilter 來確保每個 Post 操作都使用 AntiForgeryTokens?

  • February 22, 2014

我想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;
       }
   }
}

您不需要實例化任何方法AuthorizationContext或呼叫該OnAuthorization方法,只需:

if (ShouldValidateAntiForgeryTokenManually(filterContext))
{
   AntiForgery.Validate(filterContext.HttpContext, null);
}

我使用了以下方法:

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

引用自:https://stackoverflow.com/questions/11781191