【问题标题】:Posting a JSON model to ASP.Net MVC3 with Anti-forgery token使用防伪令牌将 JSON 模型发布到 ASP.Net MVC3
【发布时间】:2012-08-24 19:05:01
【问题描述】:

所以,我一直在用这个撞墙,我找不到任何好的来源。也许我忘记了模型绑定的东西在 MVC3 中是如何工作的,但这是我想要做的:我有一个与 Knockout 绑定的编辑器来处理模型的编辑。该模型没有太多内容:

public class SetupTemplate
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Template { get; set; }
} 

我试图调用的动作的签名是:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateTemplate(SetupTemplate template)

从这里的另一个问题中,我选择了这个相当有用的 sn-p 来获取防伪令牌:

window.addAntiForgeryToken = function(data) {
    data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
    return data;
};

这一切都与我一起尝试通过 ajax 发布更新:

payload = window.addAntiForgeryToken(ko.mapping.toJS(self.data));
$.ajax({
    type: "post",
    url: endpoint,
    data: payload,
    success: function(data) {
        //Handle success
    }});

这会导致 Chrome 开发者工具的表单数据部分出现这种情况

Id:1
Name:Greeting
Template: [Template Text]
__RequestVerificationToken: [The really long anti-forgery token]

防伪令牌被拾取,但我的模型为空。我见过的大多数例子只使用一个传递的参数,而不是模型。

我确定我遗漏了一些明显的东西,关于它可能是什么的任何见解?

编辑: 响应@Mark,将调用更改为:

$.ajax({
type: "post",
dataType: "json",
contentType: 'application/json',
url: endpoint,
data: JSON.stringify(payload),
success: function(data) {
    //Do some stuff
}});

产生这样的请求负载:

{"Id":1,"Name":"Greeting","Template":"...","__RequestVerificationToken":"..."}:

并且服务器没有获取防伪令牌。这在有和没有 contentType 参数到 $.ajax() 的情况下都进行了尝试。

【问题讨论】:

    标签: jquery ajax json asp.net-mvc-3 csrf


    【解决方案1】:

    映射不适用于作为模板的参数,因为它与具有相同名称(大小写)的属性之一发生冲突。如果您使用除模板之外的任何其他内容,则该控制器参数将很好地工作。

    有一个so链接解释了细节,我现在找不到了。

    public class SetupTemplate
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Template { get; set; }
    } 
    

    【讨论】:

      【解决方案2】:

      这是我的解决方案。像这样定义一个 jQuery 函数:

      (function ($) {
          $.getAntiForgeryToken = function () {
              return $('input[name="__RequestVerificationToken"]').val();
          };
      
          // (!) use ValidateJsonAntiForgeryToken attribute in your controller
          $.ajaxJsonAntiforgery = function (settings) {
      
              var headers = {};
              headers['__RequestVerificationToken'] = $.getAntiForgeryToken();
      
              settings.dataType = 'json';
              settings.contentType = 'application/json; charset=utf-8';
              settings.type = 'POST';
              settings.cache = false;
              settings.headers = headers;
              return $.ajax(settings);
          };
      })(jQuery);
      

      它只是将您的验证令牌放入标题中。您还需要过滤器属性来检查您的防伪令牌。这里是:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Web;
      using System.Web.Helpers;
      using System.Web.Mvc;
      
      namespace MyProject.Web.Infrastructure.Filters
      {
      
          [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
                          AllowMultiple = false, Inherited = true)]
          public sealed class ValidateJsonAntiForgeryTokenAttribute
                                      : FilterAttribute, IAuthorizationFilter
          {
              public void OnAuthorization(AuthorizationContext filterContext)
              {
                  if (filterContext == null)
                  {
                      throw new ArgumentNullException("filterContext");
                  }
      
                  var httpContext = filterContext.HttpContext;
                  var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
                  AntiForgery.Validate(cookie != null ? cookie.Value : null,
                                       httpContext.Request.Headers["__RequestVerificationToken"]);
              }
          }
      }
      

      在你的控制器中这真的很简单,只需用新属性(ValidateJsonAntiForgeryToken)标记它:

      [Authorize, HttpPost, ValidateJsonAntiForgeryToken]
      public ActionResult Index(MyViewModel viewModel)
      

      在客户端:

      $.ajaxJsonAntiforgery({
          data: dataToSave,
          success: function() { alert("success"); },
          error: function () { alert("error"); }
      });
      

      它对我有用。享受吧!

      【讨论】:

      • 这就像一个冠军@roman-pushkin。感谢您发布您的解决方案。
      • 一段时间后,我发现“__RequestVerificationToken”不是一个好名字。最好选择“X-RequestVerificationToken”,这样代理服务器就会让这个标头通过。
      【解决方案3】:

      你可以试试JSON.stringify吗?

      $.ajax({     
         type: "post",     
         url: endpoint,     
         data: JSON.stringify(payload),     
         success: function(data) {         
            //Handle success     
         } 
      });
      

      【讨论】:

      【解决方案4】:

      @Mark 因引导我走上正确的道路而受到赞誉,并为我指出了一些现在让我可以相当透明地处理防伪令牌的链接。但是,解决问题的方法正在发生变化:

      public ActionResult UpdateTemplate(SetupTemplate template)
      

      到:

      public ActionResult UpdateTemplate(SetupTemplate model)
      

      现在它正确地填充了值。我真的很想知道为什么会修复它,但现在它可以工作了。

      【讨论】:

        【解决方案5】:

        实际上,以下内容对我来说适用于复杂的对象;

        var application = {
            Criteria: {
                ReferenceNumber: $("input[name='Criteria.ReferenceNumber'").val()
            },
            AppliedVia: "Office"
        };
        
        // get the next step
        $.ajax({
            url: form.attr("action"),
            dataType: "html",
            type: "POST",
            data: {
                __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val(),
                application: application
            },
        }
        

        但需要注意的一点是确保data 中的左侧application 应该是您的方法/操作中的实际参数名称。这适用于 MVC 5(.NET Core 之前)

        【讨论】:

          猜你喜欢
          • 2012-01-22
          • 2015-05-11
          • 2017-05-20
          • 1970-01-01
          • 2022-08-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-26
          相关资源
          最近更新 更多