【问题标题】:How to pass AntiForgery Token to controller without a form如何在没有表单的情况下将 AntiForgery 令牌传递给控制器
【发布时间】:2019-06-14 01:24:03
【问题描述】:

我有这个 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)
{

}

【问题讨论】:

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


    【解决方案1】:
    {
    [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. 你在 url 中传递它。

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 如何获得token?并确保数据正确。
      • var token = $('input[name="__RequestVerificationToken"]').val();这就是我得到它的方式。
      • 抱歉迟到了。它在我的本地运行良好。你能分享一个可重现的演示吗?
      • 这还能用吗?控制器中的防伪检查默认忽略标题,并且正在寻找以 __ 开头的密钥
      【解决方案3】:

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

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

      【讨论】:

      • 嗯?由于未通过 [ValidateAntiForgeryToken] 部分,它甚至不会命中控制器签名
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-24
      • 2019-05-22
      • 1970-01-01
      • 2020-12-19
      • 2019-03-25
      • 2015-01-03
      • 2021-04-14
      相关资源
      最近更新 更多