Как бы я написал ActionFilter, который гарантирует, что Антифоргеритокены используются для каждой последующей операции?
Я хочу использовать 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
после каждого действия post.
Как я должен переписать свой ActionFilter, чтобы соответствовать текущим не устаревшим стандартам и правильно использовать маркер защиты от подделки на каждом [HttpPost]
глагол?
должен ли я сам включать маркер защиты от подделки в каждую форму (я думаю, что это так)? (в отличие от того, что он генерируется автоматически - не смейтесь, мне любопытно) обновление: Как указано в комментариях; Да, это должно быть сделано с каждой формой.
Вот код из моей 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;
}
}
}
2 ответа:
Вам не нужно создавать экземпляры
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:
Глобальные фильтры.Фильтры.Add(new AntiForgeryTokenFilter ());