Включить антифоргегокен в ajax пост ASP.NET MVC

У меня проблемы с AntiForgeryToken с ajax. Я использую ASP.NET MVC 3. Я пробовал решение в вызовах jQuery Ajax и Html.AntiForgeryToken (). Используя это решение, токен теперь передается:

var data = { ... } // with token, key is '__RequestVerificationToken'

$.ajax({
        type: "POST",
        data: data,
        datatype: "json",
        traditional: true,
        contentType: "application/json; charset=utf-8",
        url: myURL,
        success: function (response) {
            ...
        },
        error: function (response) {
            ...
        }
    });

Когда я удаляю [ValidateAntiForgeryToken] Атрибут только для того, чтобы увидеть, передаются ли данные (с токеном) в качестве параметров контроллеру, я вижу, что они передаются. Но по какой-то причине A required anti-forgery token was not supplied or was invalid. сообщение все еще всплывает, когда я возвращаю атрибут.

Есть идеи?

РЕДАКТИРОВАТЬ

Antiforgerytoken создается внутри формы, но я не использую действие отправки для его отправки. Вместо этого я просто получаю значение токена, используя jquery, а затем пытаюсь опубликовать это в ajax.

Вот форма, которая содержит токен и находится на верхней главной странице:

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

15 ответов

Решение

Вы неправильно указали contentType в application/json,

Вот пример того, как это может работать.

контроллер:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Index(string someValue)
    {
        return Json(new { someValue = someValue });
    }
}

Посмотреть:

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

<div id="myDiv" data-url="@Url.Action("Index", "Home")">
    Click me to send an AJAX request to a controller action
    decorated with the [ValidateAntiForgeryToken] attribute
</div>

<script type="text/javascript">
    $('#myDiv').submit(function () {
        var form = $('#__AjaxAntiForgeryForm');
        var token = $('input[name="__RequestVerificationToken"]', form).val();
        $.ajax({
            url: $(this).data('url'),
            type: 'POST',
            data: { 
                __RequestVerificationToken: token, 
                someValue: 'some value' 
            },
            success: function (result) {
                alert(result.someValue);
            }
        });
        return false;
    });
</script>

Это так просто! когда вы используете @Html.AntiForgeryToken() в вашем html-коде это означает, что сервер подписал эту страницу, и каждый запрос, отправляемый на сервер с этой конкретной страницы, имеет знак, запрещающий хакерам посылать поддельный запрос. поэтому для аутентификации этой страницы сервером вы должны пройти два шага:

1. отправить параметр с именем __RequestVerificationToken и чтобы получить его значение используйте коды ниже:

<script type="text/javascript">
    function gettoken() {
        var token = '@Html.AntiForgeryToken()';
        token = $(token).val();
        return token;
   }
</script>

например взять вызов AJAX

$.ajax({
    type: "POST",
    url: "/Account/Login",
    data: {
        __RequestVerificationToken: gettoken(),
        uname: uname,
        pass: pass
    },
    dataType: 'json',
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',
    success: successFu,
});

и шаг 2 просто украсить ваш метод действия [ValidateAntiForgeryToken]

Другой (менее явский) подход, который я использовал, выглядит примерно так:

Во-первых, Html помощник

public static MvcHtmlString AntiForgeryTokenForAjaxPost(this HtmlHelper helper)
{
    var antiForgeryInputTag = helper.AntiForgeryToken().ToString();
    // Above gets the following: <input name="__RequestVerificationToken" type="hidden" value="PnQE7R0MIBBAzC7SqtVvwrJpGbRvPgzWHo5dSyoSaZoabRjf9pCyzjujYBU_qKDJmwIOiPRDwBV1TNVdXFVgzAvN9_l2yt9-nf4Owif0qIDz7WRAmydVPIm6_pmJAI--wvvFQO7g0VvoFArFtAR2v6Ch1wmXCZ89v0-lNOGZLZc1" />
    var removedStart = antiForgeryInputTag.Replace(@"<input name=""__RequestVerificationToken"" type=""hidden"" value=""", "");
    var tokenValue = removedStart.Replace(@""" />", "");
    if (antiForgeryInputTag == removedStart || removedStart == tokenValue)
        throw new InvalidOperationException("Oops! The Html.AntiForgeryToken() method seems to return something I did not expect.");
    return new MvcHtmlString(string.Format(@"{0}:""{1}""", "__RequestVerificationToken", tokenValue));
}

это вернет строку

__RequestVerificationToken:"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1"

так что мы можем использовать это так

$(function () {
    $("#submit-list").click(function () {
        $.ajax({
            url: '@Url.Action("SortDataSourceLibraries")',
            data: { items: $(".sortable").sortable('toArray'), @Html.AntiForgeryTokenForAjaxPost() },
            type: 'post',
            traditional: true
        });
    });
});

И это похоже на работу!

В Asp.Net Core вы можете запросить токен напрямую, как описано в документации:

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf    
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}

И используйте это в JavaScript:

function DoSomething(id) {
    $.post("/something/todo/"+id,
               { "__RequestVerificationToken": '@GetAntiXsrfRequestToken()' });
}

Вы можете добавить рекомендуемый глобальный фильтр, как описано в документации:

services.AddMvc(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
})

В Asp.Net MVC при использовании @Html.AntiForgeryToken() Razor создает скрытое поле ввода с именем __RequestVerificationToken хранить токены. Если вы хотите написать реализацию AJAX, вы должны сами извлечь этот токен и передать его в качестве параметра на сервер, чтобы его можно было проверить.

Шаг 1: Получить токен

var token = $('input[name="`__RequestVerificationToken`"]').val();

Шаг 2: Передайте токен в вызове AJAX

function registerStudent() {

var student = {     
    "FirstName": $('#fName').val(),
    "LastName": $('#lName').val(),
    "Email": $('#email').val(),
    "Phone": $('#phone').val(),
};

$.ajax({
    url: '/Student/RegisterStudent',
    type: 'POST',
    data: { 
     __RequestVerificationToken:token,
     student: student,
        },
    dataType: 'JSON',
    contentType:'application/x-www-form-urlencoded; charset=utf-8',
    success: function (response) {
        if (response.result == "Success") {
            alert('Student Registered Succesfully!')

        }
    },
    error: function (x,h,r) {
        alert('Something went wrong')
      }
})
};

Примечание: тип контента должен быть 'application/x-www-form-urlencoded; charset=utf-8'

Я загрузил проект на Github; Вы можете скачать и попробовать.

https://github.com/lambda2016/AjaxValidateAntiForgeryToken


        function DeletePersonel(id) {

                var data = new FormData();
                data.append("__RequestVerificationToken", "@HtmlHelper.GetAntiForgeryToken()");

                $.Ajax({
                    тип: 'POST',
                    url: '/Personel/Delete/' + id,
                    данные: данные,
                    кеш: ложь,
                    processData: false,
                    contentType: false,
                    success: function (result) {

                    }
                });

        }
    

        открытый статический класс HtmlHelper
        {
            публичная статическая строка GetAntiForgeryToken()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), "(?: Value=\")(.*)(?:\")");
                если (значение. Успех)
                {
                    возвращаемое значение. Группы [1]. Значение;
                }
                вернуть "";
            }
        }

В контроллере аккаунта:

    // POST: /Account/SendVerificationCodeSMS
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public JsonResult SendVerificationCodeSMS(string PhoneNumber)
    {
        return Json(PhoneNumber);
    }

Ввиду:

$.ajax(
{
    url: "/Account/SendVerificationCodeSMS",
    method: "POST",
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',
    dataType: "json",
    data: {
        PhoneNumber: $('[name="PhoneNumber"]').val(),
        __RequestVerificationToken: $('[name="__RequestVerificationToken"]').val()
    },
    success: function (data, textStatus, jqXHR) {
        if (textStatus == "success") {
            alert(data);
            // Do something on page
        }
        else {
            // Do something on page
        }
    },
    error: function (jqXHR, textStatus, errorThrown) {
        console.log(textStatus);
        console.log(jqXHR.status);
        console.log(jqXHR.statusText);
        console.log(jqXHR.responseText);
    }
});

Важно установить contentType в 'application/x-www-form-urlencoded; charset=utf-8' или просто опустить contentTypeс объекта...

Для меня решение состояло в том, чтобы отправить токен в виде заголовка, а не в виде данных в вызове ajax:

          $.ajax({
       type: "POST",
       url: destinationUrl,
       data: someData,
       headers:{
           "RequestVerificationToken": token
       },
       dataType: "json",
       success: function (response) {
           successCallback(response);
       },
       error: function (xhr, status, error) {
          // handle failure
       }
   });

Я знаю, что это старый вопрос. Но я все равно добавлю свой ответ, может помочь кому-то вроде меня.

Если вы не хотите обрабатывать результат из действия post контроллера, например, вызов LoggOff метод Accounts контроллер, вы можете сделать как следующую версию ответа @DarinDimitrov:

@using (Html.BeginForm("LoggOff", "Accounts", FormMethod.Post, new { id = "__AjaxAntiForgeryForm" }))
{
    @Html.AntiForgeryToken()
}

<!-- this could be a button -->
<a href="#" id="ajaxSubmit">Submit</a>

<script type="text/javascript">
    $('#ajaxSubmit').click(function () {

        $('#__AjaxAntiForgeryForm').submit();

        return false;
    });
</script>

Токен не будет работать, если он был предоставлен другим контроллером. Например, это не сработает, если представление было возвращеноAccounts контроллер, но ты POST к Clients контроллер.

Я боролся с этим и придумал решение, которое одновременно является декларативным и не требует большого количества изменений клиента или сервера . Это работает для ASPNET MVC .NET 4.8, но можно легко сделать и в .NET Core.

Вкратце:

  • Используйте ASPNET MVC AntiForgery.Validate() и AntiForgery.GetTokens().
  • Сохраняйте токен на странице как скрытое поле.
  • Получите токен со страницы и прозрачно «протолкните» его по всем вызовам JSON, используя jQuery.ajaxSend() .
  • Обработайте полученный токен, используя ValidateAntiForgeryJSONToken (пользовательский атрибут FilterAttribute).

Преимущества:

  • Просто украсьте действия контроллера [ValidateAntiForgeryJSONToken].
  • Добавьте токен один раз.
  • Централизованно перемещайте токен по JSON.

Вот код:

Атрибут ValidateAntiForgeryJSONToken

          [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public class ValidateAntiForgeryJSONToken : FilterAttribute, IAuthorizationFilter
    {
        public ValidateAntiForgeryJSONToken()
        {
        }

        public virtual void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
                throw new ArgumentNullException(nameof(filterContext));

            var request = filterContext.RequestContext.HttpContext.Request;
            if (request == null)
                return;

            var stream = request.InputStream;
            var encoding = request.ContentEncoding;
            var reader = new StreamReader(stream, encoding);
            var json = reader.ReadToEnd();
            request.InputStream.Position = 0;
            var token = "";
            try
            {
                JObject o = JObject.Parse(json);
                var jToken = o.GetValue("ajaxAFT", StringComparison.InvariantCultureIgnoreCase);
                token = jToken.ToString();
            }
            catch (Exception)
            {
            }

            ValidateAntiForgeryToken(token);
        }

        public static void ValidateAntiForgeryToken(string token)
        {
            string cookieToken = "";
            string formToken = "";

            if (!String.IsNullOrWhiteSpace(token))
            {
                string[] tokens = token.Split(':');
                if (tokens.Length == 2)
                {
                    cookieToken = tokens[0].Trim();
                    formToken = tokens[1].Trim();
                }
            }

            try
            {
                AntiForgery.Validate(cookieToken, formToken);
            }
            catch (Exception)
            {
                throw new BaseException();
            }
        }
    }

Контроллер

       
        [HttpPost]
        [ValidateAntiForgeryJSONToken]
        public ActionResult TestAJAXCall(string id)
        {
        }

Перехват Аякса

      'use strict';

$(document)
    .ajaxSend(function (event, jqXHR, ajaxSettings) {
        if (ajaxSettings !== undefined) {
            try {
                let payload = JSON.parse(ajaxSettings.data);
                let token = payload.ajaxAFT;
                if (token === undefined) {
                    token = $("#_ajaxAFT").val();
                    payload.ajaxAFT = token;
                    ajaxSettings.data = JSON.stringify(payload);
                }
            } catch (e) {

            }
        }
    });

Внедрение токена Razor

       @Html.AjaxAntiForgeryToken()

Помощник по токенам Razor

      public static HtmlString AjaxAntiForgeryToken(this HtmlHelper helper)
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            var myToken = cookieToken + ":" + formToken;

            var model = new FormHiddenInputFormModel()
            {
                Id = "_ajaxAFT",
                Value = myToken

            };
            return helper.Partial("Forms/_FormHiddenInputText", model);
        }

Создайте метод, который будет отвечать за добавление токена

      var addAntiForgeryToken = function (data) {
    data.__RequestVerificationToken = $("[name='__RequestVerificationToken']").val();
    return data;
};

Теперь используйте этот метод при передаче данных / параметров в Action, как показано ниже.

       var Query = $("#Query").val();
        $.ajax({
            url: '@Url.Action("GetData", "DataCheck")',
            type: "POST",
            data: addAntiForgeryToken({ Query: Query }),
            dataType: 'JSON',
            success: function (data) {
            if (data.message == "Success") {
            $('#itemtable').html(data.List);
            return false;
            }
            },
            error: function (xhr) {
            $.notify({
            message: 'Error',
            status: 'danger',
            pos: 'bottom-right'
            });
            }
            });

Здесь у моего действия есть единственный параметр строкового типа

          [HttpPost]
    [ValidateAntiForgeryToken]
    public JsonResult GetData( string Query)
    {
       @using (Ajax.BeginForm("SendInvitation", "Profile",
        new AjaxOptions { HttpMethod = "POST", OnSuccess = "SendInvitationFn" },
        new { @class = "form-horizontal", id = "invitation-form" }))
    {
        @Html.AntiForgeryToken()
        <span class="red" id="invitation-result">@Html.ValidationSummary()</span>

        <div class="modal-body">
            <div class="row-fluid marg-b-15">
                <label class="block">                        
                </label>
                <input type="text" id="EmailTo" name="EmailTo" placeholder="forExample@gmail.com" value="" />
            </div>
        </div>

        <div class="modal-footer right">
            <div class="row-fluid">
                <button type="submit" class="btn btn-changepass-new">send</button>
            </div>
        </div>
    }

Я перепробовал много обходных путей, и ни один из них не помог мне. Исключением было "Обязательное поле формы защиты от подделки"__RequestVerificationToken" .

Что помогло мне, так это переключить форму.ajax на.post:

$.post(
    url,
    $(formId).serialize(),
    function (data) {
        $(formId).html(data);
    });

Не стесняйтесь использовать функцию ниже:

function AjaxPostWithAntiForgeryToken(destinationUrl, successCallback) {
var token = $('input[name="__RequestVerificationToken"]').val();
var headers = {};
headers["__RequestVerificationToken"] = token;
$.ajax({
    type: "POST",
    url: destinationUrl,
    data: { __RequestVerificationToken: token }, // Your other data will go here
    dataType: "json",
    success: function (response) {
        successCallback(response);
    },
    error: function (xhr, status, error) {
       // handle failure
    }
});

}

Другие вопросы по тегам