如何在没有表单的情况下将AntiForgery令牌传递给控制器

时间:2019-06-14 01:24:03

标签: javascript jquery asp.net asp.net-core

我有这个jquery函数,我想将防伪令牌传递给要验证的控制器。但是,无论我是通过标头还是通过正文,我当前的方法都将返回错误400。

我将此空白表格放在页面顶部

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "form" }))
{
    @Html.AntiForgeryToken()
}

这是jquery函数。我在标头和正文中都有requestverificationtoken,但是它不起作用。

$("#eventGenerateButton").on("click", function (e) {
    e.preventDefault();
    var event_form_data = {
        "__RequestVerificationToken": token,
        "StartDate": $("#eventStartDate").val(),
        "EndDate": $("#eventEndDate").val(),
    };

    $.ajax({
        url: "@Url.Action("GenerateEventLogsReport", @ViewContext.RouteData.Values["controller"].ToString())",
        method: "POST",
        headers: { "__RequestVerificationToken": token},
        xhrFields: {
            responseType: 'blob'
        },
        data: JSON.stringify(event_form_data),
        contentType: "application/json",
        success: function (result) {
            GenerateReport(result,"EventLogs");
        },
        error: function (error) {
            console.log(error);
        }
    });
    return false;
});

这是控制器

[HttpPost]
[ValidateAntiForgeryToken]
public FileResult GenerateEventLogsReport([FromBody]GenericReportDateViewModel Input)
{

}

3 个答案:

答案 0 :(得分:0)

尝试将您的控制器签名更改为此:

public FileResult GenerateEventLogsReport(string __RequestVerificationToken, string StartDate, string EndDate)
{

}

答案 1 :(得分:0)

event_form_data删除令牌,然后尝试发送请求标头,如

var event_form_data = {
    "StartDate": $("#eventStartDate").val(),
    "EndDate": $("#eventEndDate").val(),
    };
var token = $('input[name="__RequestVerificationToken"]').val();
$.ajax({
    url: "/Home/AjaxPost",
    method: "POST",      
    headers: { "RequestVerificationToken": token},
    xhrFields: {
        responseType: 'blob'
    },
    data: JSON.stringify(event_form_data),
    contentType: "application/json",
    success: function (result) {

    },
    error: function (error) {
        console.log(error);
    }
});

答案 2 :(得分:0)

{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
            AllowMultiple = false, Inherited = true)]
///this attribute is for validating antiforgery, no matter how it's sent.
///It doesn't care if the token is in the header or body, and the body can be stringified.
public sealed class MyValidateAntiForgeryAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
        
        var httpContext = filterContext.HttpContext;
        
            /* Note:  Antiforgery tokens are passed twice from a page.  Once from
             * the post itself, and once as a cookie to verify against it.  The
             * values do not match.  They are two halves of a whole and both get
             * passed into the validation routine. */
            var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
            //try header first
            var token = httpContext.Request.Headers["__RequestVerificationToken"];
            //try normal method
            if (token == null)
            {
                for (int cl = 0; cl < httpContext.Request.Form.Keys.Count; cl++)
                {
                    if (httpContext.Request.Form.Keys[cl] == "__RequestVerificationToken")
                    {
                        token = httpContext.Request.Form[httpContext.Request.Form.Keys[cl]];
                    }
                }
            }
            //try url
            if (token == null)
            {
                for (int cl = 0; cl < httpContext.Request.QueryString.Keys.Count; cl++)
                {
                    if (httpContext.Request.QueryString.Keys[cl] == "__RequestVerificationToken")
                    {
                        token = httpContext.Request.QueryString[httpContext.Request.QueryString.Keys[cl]];
                    }
                }
            }
            /* if still null, check to see if it's in a json string.
               this section will retrieve the token out of a serialized dataset.
            */
            if (token == null)
            {
                string json;
                var position = httpContext.Request.InputStream.Position;
                using (var reader = new StreamReader(httpContext.Request.InputStream, Encoding.UTF8, false, 8192, true))
                {
                    json = reader.ReadToEnd();
                    //you MUST reset the input stream to start or you will break post.
                    httpContext.Request.InputStream.Seek(position, SeekOrigin.Begin);
                    try
                    {
                        var jsonObj = Json.Decode(json); //attempt to parse into json object.  
                        token = jsonObj["__RequestVerificationToken"];
                    }
                    catch
                    {
                        //eat the parse errors.  That simply means it's not json.
                    }
                }
            }
            AntiForgery.Validate(cookie?.Value, token);
        
    }
}

在这些情况下,您无法使用股票验证器验证防伪令牌:

  1. json 已序列化。
  2. 在标题中传递它。
  3. 您在网址中传递它。

如果您需要执行这些操作中的任何一项,您需要编写自己的防伪检查,以便您可以在传递的数据中找到令牌。 我写了一份防伪检查,执行以下操作:

  1. 检查 url 中的令牌
  2. 在序列化数据中检查它
  3. 在标题中检查它。 随意使用它。请注意,这(故意)并没有过滤掉 get。如果您在班级级别添加它,您可能想要添加它。