在ASP.NET MVC应用程序中使用AngularJS时,为什么AntiForgeryToken验证失败?

时间:2016-04-29 14:52:52

标签: angularjs asp.net-mvc csrf-protection antiforgerytoken asp.net-mvc-5.1

在ASP.NET MVC应用程序中使用AngularJS $ http服务时,我遇到了一些传递正确AntiForgertyToken的问题。

我尝试了以下内容:

  1. 使用httpInterceptor

    设置HTTP请求标头
    app.factory('httpInterceptorService', function ($q) {
        return {
            'request': function (config) {
                blockUI();
                config.headers['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; // Disables IE AJAX request caching
                config.headers['Cache-Control'] = 'no-cache';
                config.headers['Pragma'] = 'no-cache';
                config.headers['X-Requested-With'] = 'XMLHttpRequest';
                config.headers['__RequestVerificationToken'] = $('[name=__RequestVerificationToken]').val();
                return config;
            },
    
  2. 通过工厂服务设置HTTP请求标头

    app.factory('networkService', function ($http) {
        return {
            postDataAsAjax: function (url, params) {
                debugger;
                return $http({
                    method: 'POST',
                    url: url,
                    data: params,
                    headers: {
                        '__RequestVerificationToken': $('[name=__RequestVerificationToken]').val(),
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                }).then(function (result) {
    
  3. 这两种方法都抛出AntiForgeryTokenException。

    有没有其他方法可以实现这个目标?

    编辑(添加HTTP请求信息)

    POST /WebApplication1/Home/Index HTTP/1.1
    Host: localhost
    Connection: keep-alive
    Content-Length: 799
    Cache-Control: no-cache
    Pragma: no-cache
    Origin: http://localhost
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36
    Content-Type: application/json;charset=UTF-8
    Accept: application/json, text/plain, */*
    X-Requested-With: XMLHttpRequest
    If-Modified-Since: Mon, 26 Jul 1997 05:00:00 GMT
    __RequestVerificationToken: CKCARSoIug5mHnHmUT4ciSmf3pCk1YJkcwq3czo5snfEwTVPBUYLQj7z7w3KKDu001RYk7zuMZ1LEwwWB1tNpZR0agxJK1DjqjMDnQNewLKGCmExANXIJ-Du7lc0LEFw0
    Referer: http://localhost/SSP-Working_SourceCode/
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.8
    Cookie: __ngDebug=true; ASP.NET_SessionId=4kcpxz1oj0042ndw4aotx0jl; __RequestVerificationToken_L1NTUC1Xb3JraW5nX1NvdXJjZUNvZGU1=Q8FOz7jJHQHdes02AJvRGglFU_pcz5eqcnZY3QXg37z9k1LMYiPWq-kKbXlYCbAfK0IgLpCtpBax6w-rB1J_NBi7KzGyCuwLCHjKNREjMhQ1; .ASPXFORMSAUTH=CC35114F38FD17866FAF38A1FDC525263A0858EFECFB03AEEED7E9AF7FAA2995262A426D4AA50EB87C47969C3C191BC9B3D31BC67A831C099F286AD3013348B14659632BC54425E3D81C19CB382E175B2DA3755DDFE46D7A79810FB79EBE832D616A299C93CFDA2105576B922C6A1D111A23BB6F9594532C310A15AF2162785A
    

    编辑(添加了自定义防伪标记属性)

    public class GlobalAntiForgeryTokenAttribute : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationContext authorizationContext)
        {
            var request = authorizationContext.HttpContext.Request;
    
            if (request.HttpMethod.ToUpper() != "POST")
            {
                return;
            }
    
            if (authorizationContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
                authorizationContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
            {
                return;
            }
    
            if (request.IsAjaxRequest())
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
                var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null;
                AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute().OnAuthorization(authorizationContext);
            }
        }
    }
    

    编辑(添加HTTP响应)

      

    System.Web.Mvc.HttpAntiForgeryException :所需的防伪表单字段__RequestVerificationToken不存在。

2 个答案:

答案 0 :(得分:1)

问题在于验证令牌是表单数据的一部分,但您在标题中提供它。

This post告诉您如何构建一个用于验证标头的属性过滤器。

这是我的:

[AttributeUsage(AttributeTargets.Class)]
    public class ValidateAntiForgeryTokenOnAjax : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var request = filterContext.HttpContext.Request;

            //  Only validate POSTs
            if (request.HttpMethod == WebRequestMethods.Http.Post)
            {
                //  Ajax POSTs and normal form posts have to be treated differently when it comes
                //  to validating the AntiForgeryToken
                if (request.IsAjaxRequest())
                {
                    var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                    var cookieValue = antiForgeryCookie != null
                        ? antiForgeryCookie.Value
                        : null;

                    AntiForgery.Validate(cookieValue, request.Headers[AntiForgeryConfig.CookieName]);
                }
                else
                {
                    new ValidateAntiForgeryTokenAttribute()
                        .OnAuthorization(filterContext);
                }
            }
        }

然后按照以下方式配置角度:

var myApp = angular.module("myApp", ["ngRoute"])
    .run(function ($http) {
        $http.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
        $http.defaults.headers.post["__RequestVerificationToken"] = $("#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]").val();
    });

在我的主布局页面上,我声明了这个表单:

<form id="__AjaxAntiForgeryForm" action="#" method="post">@Html.AntiForgeryToken()</form>

答案 1 :(得分:0)

对于那些面临类似问题的人来说,我们的应用程序的问题是前一个开发人员还在requeset POSTing到的MVC控制器操作上定义了默认的“ValidateAntiForgeryToken”属性。

一旦我们从控制器操作中删除了[ValidationAntiForgeryToken],它就开始工作了。

我们有一个自定义防伪标记属性,用于检查标头中的标记,但MVC属性的默认实现仅检查请求主体,这导致此操作失败。