如何在 Ajax header 中使用 MVC6 RC1 RC2 中的 IAuthorizationFilter 验证防伪令牌

How to Validate Antiforgery token in Ajax header using IAuthorizationFilter in MVC6 RC1 RC2

如果您使用 RC2 忽略所有过滤器代码,您需要做的就是

StartUp.cs

  services.AddAntiforgery(options => options.HeaderName = "YOUR_HEADER_NAME");

RC1

以前我使用 IAuthorization 过滤器来验证 AntiForgery 令牌,但似乎在 MVC6 中情况发生了巨大变化。我找不到任何描述过滤更改的文档。

这是我的旧代码

  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class CustomValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
    // why is this necessary..
    // take a look here http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-%28csrf%29-attacks
    // the CSRF token is inserted into the AJAX header
    //
    public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        if (actionContext == null)
        {
            throw new ArgumentNullException("actionContext");
        }
        if (continuation == null)
        {
            throw new ArgumentNullException("continuation");
        }

        try
        {
            // insanely easy -- just extart the cookie token and the headertoken - it they are null who cares the validation will fail
            var headerToken = actionContext.Request.Headers.GetValues("__RequestVerificationToken").FirstOrDefault(); // ajax headers
            var cookieToken = actionContext.Request.Headers.GetCookies().Select(s => s[AntiForgeryConfig.CookieName]).FirstOrDefault();

            AntiForgery.Validate(cookieToken.Value, headerToken);
        }
        catch (HttpAntiForgeryException)
        {
            actionContext.Response = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.Forbidden,
                RequestMessage = actionContext.ControllerContext.Request
            };
            return FromResult(actionContext.Response);
        }
        return continuation();
    }

    private Task<HttpResponseMessage> FromResult(HttpResponseMessage result)
    {
        var source = new TaskCompletionSource<HttpResponseMessage>();
        source.SetResult(result);
        return source.Task;
    }
}

 [Authorize]
public class SubmissionController : ApiController
{
    [CustomValidateAntiForgeryToken] // note this is a custom filter, named to clarify its not the built in one as thats for views!
    public HttpResponseMessage Delete(int? ID)  {.. blah ..}

谢谢

这是一个基于 MVC 文档的示例实现。

public class ValidateAntiForgeryHeaderToken : TypeFilterAttribute
{
    public ValidateAntiForgeryHeaderToken() : base(typeof(ValidateAntiForgeryHeaderTokenImpl))
    {
    }

    // see https://docs.asp.net/en/latest/mvc/controllers/filters.html
    //
    // If you have a simple filter that doesn’t require any arguments, but which has constructor dependencies that need to be filled by DI, 
    // you can inherit from TypeFilterAttribute, allowing you to use your own named attribute on classes and methods (instead of [TypeFilter(typeof(FilterType))]). 
    //

    private class ValidateAntiForgeryHeaderTokenImpl : IAsyncAuthorizationFilter
    {
        private readonly IAntiforgery _antiforgery; 

        public readonly string verificationToken = "X-VerificationToken";
        public readonly string antiforgeryCookieName;

        public ValidateAntiForgeryHeaderTokenImpl(IAntiforgery antiforgery, IOptions<AntiforgeryOptions> antiforgeryOptions)
        {
            _antiforgery = antiforgery;
            antiforgeryCookieName = antiforgeryOptions.Value.CookieName; // if not specified this is autogenerated by mvc
        }

        public Task OnAuthorizationAsync(AuthorizationContext context)
        {
            string headerToken = context.HttpContext.Request.Headers[verificationToken];
            if (headerToken != null)
            {                     
                string antiForgeryCookieValue = context.HttpContext.Request.Cookies[antiforgeryCookieName];
                _antiforgery.ValidateTokens(context.HttpContext, new AntiforgeryTokenSet(headerToken, antiForgeryCookieValue)); // throws on invalid 
                return Task.FromResult<object>(null);
            }
            return _antiforgery.ValidateRequestAsync(context.HttpContext);               
        }
    }
}

装饰你的controller/action

    [HttpPost]
    [ValidateAntiForgeryHeaderToken]
    public IActionResult Add([FromBody] someObject data )
    {
        // Why Frombody??  https://lbadri.wordpress.com/2014/11/23/web-api-model-binding-in-asp-net-mvc-6-asp-net-5/
    }

并在您的 AJAX 调用中,将从 @Html.AntiForgeryToken() 生成的令牌放入请求 header 中。

$.ajax({
type: 'POST',
url: '@Url.Action("yourAction", "yourController", new { Area = "yourArea" })',
dataType: "json",
contentType: "application/json",
data: JSON.stringify(data),
headers: {
    'X-VerificationToken': $("[name='__RequestVerificationToken']").val()
},