究竟要把防伪手表放在哪里

时间:2014-04-01 08:50:01

标签: javascript asp.net-mvc angularjs http http-headers

我有一个布局页面,其中包含一个带有AntiForgeryToken

的表单
using (Html.BeginForm(action, "Account", new { ReturnUrl = returnUrl }, FormMethod.Post, new { Id = "xcrf-form" }))

这会生成隐藏字段

<input name="__RequestVerificationToken" type="hidden" value="p43bTJU6xjctQ-ETI7T0e_0lJX4UsbTz_IUjQjWddsu29Nx_UE5rcdOONiDhFcdjan88ngBe5_ZQbHTBieB2vVXgNJGNmfQpOm5ATPbifYE1">

在我的角度视图中(在布局页面的div中加载,我这样做

<form class="form" role="form" ng-submit="postReview()">

我的postReview()代码如下

$scope.postReview = function () {
    var token = $('[name=__RequestVerificationToken]').val();

    var config = {
        headers: {
            "Content-Type": "multipart/form-data",
            // the following when uncommented does not work either
            //'RequestVerificationToken' : token
            //"X-XSRF-TOKEN" : token
        }
    }

    // tried the following, since my other MVC controllers (non-angular) send the token as part of form data, this did not work though
    $scope.reviewModel.__RequestVerificationToken = token;

    // the following was mentioned in some link I found, this does not work either
    $http.defaults.headers.common['__RequestVerificationToken'] = token;

    $http.post('/Review/Create', $scope.reviewModel, config)
    .then(function (result) {
        // Success
        alert(result.data);
    }, function (error) {
        // Failure
        alert("Failed");
    });
}

我的MVC Create方法如下

    [HttpPost]
    [ValidateAntiForgeryToken]
    [AllowAnonymous]
    public ActionResult Create([Bind(Include = "Id,CommentText,Vote")] ReviewModel reviewModel)
    {
        if (User.Identity.IsAuthenticated == false)
        {
            // I am doing this instead of [Authorize] because I dont want 302, which browser handles and I cant do client re-direction
            return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
        }

        // just for experimenting I have not yet added it to db, and simply returning
        return new JsonResult {Data = reviewModel, JsonRequestBehavior = JsonRequestBehavior.AllowGet};
    }

所以无论我在哪里放置令牌,无论我使用什么'Content-Type'(我尝试过application-json和www-form-urlencoded)我总是得到错误“所需的防伪表单字段” __RequestVerificationToken“不存在。”

我甚至试过命名__RequestVerificationToken和RequestVerificationToken

为什么我的服务器找不到该死的令牌?

我还看了几个链接,要求你实现自己的AntiForgeryToeknVerifyAttrbute并验证作为cookieToken发送的令牌:formToken,我没有尝试过,但为什么我无法让它工作,而这适用于MVC控制器(非角柱)

4 个答案:

答案 0 :(得分:5)

是。默认情况下,MVC框架将检查Request.Form["__RequestVerificationToken"]

检查 MVC source code

    public AntiForgeryToken GetFormToken(HttpContextBase httpContext)
    {
        string value = httpContext.Request.Form[_config.FormFieldName];
        if (String.IsNullOrEmpty(value))
        {
            // did not exist
            return null;
        }

        return _serializer.Deserialize(value);
    }

您需要创建自己的过滤器,以便从Request.Header

进行检查

Code Snippet from Phil Haack's Article - MVC 3

private class JsonAntiForgeryHttpContextWrapper : HttpContextWrapper {
  readonly HttpRequestBase _request;
  public JsonAntiForgeryHttpContextWrapper(HttpContext httpContext)
    : base(httpContext) {
    _request = new JsonAntiForgeryHttpRequestWrapper(httpContext.Request);
  }

  public override HttpRequestBase Request {
    get {
      return _request;
    }
  }
}

private class JsonAntiForgeryHttpRequestWrapper : HttpRequestWrapper {
  readonly NameValueCollection _form;

  public JsonAntiForgeryHttpRequestWrapper(HttpRequest request)
    : base(request) {
    _form = new NameValueCollection(request.Form);
    if (request.Headers["__RequestVerificationToken"] != null) {
      _form["__RequestVerificationToken"] 
        = request.Headers["__RequestVerificationToken"];
    }
}

  public override NameValueCollection Form {
    get {
      return _form;
    }
  }
}

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, 
    AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : 
    FilterAttribute, IAuthorizationFilter {
  public void OnAuthorization(AuthorizationContext filterContext) {
    if (filterContext == null) {
      throw new ArgumentNullException("filterContext");
    }

    var httpContext = new JsonAntiForgeryHttpContextWrapper(HttpContext.Current);
    AntiForgery.Validate(httpContext, Salt ?? string.Empty);
  }

  public string Salt {
    get;
    set;
  }

  // The private context classes go here
}

请在此处查看MVC 4 implementation,以避免salt问题

[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"]);
    }
}

答案 1 :(得分:1)

我遇到了同样的问题。原来我不需要在我的角度js代码中明确地设置防伪令牌。 MVC控制器期望此标记值从1.表单字段,2。cookie传递。过滤器等同并且在匹配时很开心。 当我们提交表单时,防伪标记的隐藏字段会自动提供其值。 Cookie由浏览器自动设置。正如我所说,我们不需要明确地做任何事情。

问题实际上是请求的内容类型。默认情况下,它与application / json一样,因此是a.f.未收到令牌值(或更确切地说是任何表单数据)。 以下为我工作:

// create the controller
var RegisterController = function ($scope, $http) {

    $scope.onSubmit = function (e) {
        // suppress default form submission
        e.preventDefault();
        var form = $("#registerform");

        if (form.valid()) {
            var url = form.attr('action');
            var data = form.serialize();

            var config = {
                headers: {
                    'Content-type':'application/x-www-form-urlencoded',
                }
            };

            $http.post(url, data, config).success(function (data) {
                alert(data);
            }).error(function(reason) {
                alert(reason);
            });

        }
    };
};

答案 2 :(得分:0)

正如Murali建议我想我需要将toekn放在表单中,所以我尝试将令牌作为表单数据的一部分,我需要按照https://stackoverflow.com/a/14868725/2475810

中的说明对表单数据进行编码

此方法不需要服务器端的任何其他代码,我们也不需要创建和加入cookie和表单令牌。只需通过对数据进行形式编码并将令牌作为上述答案中的一个字段包含在内,我们就可以推动它了。

答案 3 :(得分:0)

您应该以这种方式执行HTTP请求:

$http({
    url: '/Review/Create',
    data: "__RequestVerificationToken=" + token + "&param1=1&param2=2",
    method: 'POST',
    headers: {
      'X-Requested-With': 'XMLHttpRequest',
      'Accept': 'application/json, text/javascript, */*; q=0.01',
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
  }).success(function(result) {
    alert(result.data);
  }).error(function(error) {
    alert("Failed");
  });