включити antiforgerytoken в ajax пост ASP.NET MVC


168

У мене проблеми з AntiForgeryToken з аяксом. Я використовую 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.повідомлення все-таки спливає, коли я повертаю атрибут назад.

Будь-які ідеї?

EDIT

Антифоргектотекен генерується у формі, але я не використовую для надсилання дії. Натомість я просто отримую значення маркера за допомогою jquery, а потім намагаюся надіслати повідомлення в Ajax.

Ось форма, що містить маркер і розташована у верхній головній сторінці:

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

Відповіді:


289

Ви неправильно вказані 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>

Привіт, дякую за швидку відповідь. Вибачте, що не згадав про це у питанні; Наразі не використовую дію надсилання. (Маркер знаходиться у формі, але я не використовую кнопку "Надіслати", щоб подати його. Чи можливо просто змінити тип вмісту на щось інше?
OJ Raqueño

13
Те, що ви не використовуєте дію для надсилання, не дуже змінює мою відповідь. Все, що вам потрібно зробити, це підписатися на якусь іншу подію (натискання кнопки, натискання якоря чи що завгодно і просто прочитати приховане значення поля). Що стосується надсилання запиту AJAX, ви можете використовувати приклад, наведений у моїй відповіді. Ви не повинні використовувати , contentTypeщоб , application/jsonтому що сервер очікує , що __RequestVerificationTokenпараметр , щоб бути частиною корисного навантаження запиту POST з використанням application/x-www-form-urlencoded.
Дарин Димитров

як цей код $(this).data('url'),може зрозуміти, якою буде URL-адреса мого контролера та дії. Будь ласка, поясніть. дякую
Mou

2
Оригінальне питання стосувалось contentType: 'application / json'. Для регулярних публікацій ajax, включаючи __RequestVerificationToken у формі повідомлення, очевидно, буде працювати, оскільки це як звичайний пост у формі. Якщо ви хочете опублікувати json (отже, тип вмісту), схоже, це не спрацює. Тож це випадок неправильного прийняття сказаного як відповіді.
Іван

Чи потрібно використовувати "ModelState.IsValid"? Як я можу сказати, що це працює?
Моран Монович

61

Інший (менш javascriptish) підхід, який я зробив, має щось подібне:

По-перше, помічник 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
        });
    });
});

І, здається, працює!


5
+1, приємно. Я просто розділив @Html.AntiForgeryTokenForAjaxPostдва, щоб отримати назву лексеми в одній руці та її значення в іншій. В іншому випадку виділення синтаксису все переплутано. Це закінчується так (видалено одноразові цитати з повернутого результату, так що він поводиться як будь-який помічник MVC, наприклад @Url):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein

4
ніт приємно. З цим у вас є файл ajax call n cshtm .... ви не повинні так сильно заважати js з бритвою.
зайчик1985

Я спростував це питання, оскільки вважаю, що більш простим підходом є використання статичного класу AntiForgery. Отримати HTML і замінити його замість того, щоб безпосередньо отримати значення лексеми - це погана практика. ASP.NET є повністю відкритим вихідним кодом: github.com/ASP-NET-MVC/aspnetwebstack/blob/… (але тепер можна було б написати ще одну відповідь за допомогою спеціального методу розширення, який отримує лише маркер)
usr-local- ΕΨΗΕΛΩΝ

4
Більш чистим способом отримання лише значення лексеми було б використання XElement. XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui

3
@transformervar antiForgeryInputTag = helper.AntiForgeryToken().ToString(); return XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui

45

це так просто! коли ви використовуєте @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]


Дякую, чудово працює для json публікації ... мені не вистачало contentType :(
Snziv Gupta

9

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

Оновлення

Вищевказане рішення працює в скриптах, що входять до .cshtml. Якщо це не так, ви не можете використовувати це безпосередньо. Моє рішення полягало у використанні прихованого поля для зберігання значення спочатку.

Моє вирішення все ще використовується GetAntiXsrfRequestToken :

Коли немає форми:

<input type="hidden" id="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">

nameАтрибут може бути опущений , так як я використовую idатрибут.

Кожна форма включає цей маркер. Тож замість того, щоб додавати ще одну копію того ж маркера у приховане поле, ви також можете шукати наявне поле за допомогою name. Зверніть увагу: всередині документа може бути кілька форм, тому nameвоно не є унікальним. На відміну від idатрибута, який повинен бути унікальним.

У сценарії знайдіть за id:

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

Альтернативою, без посилання на маркер, є подання форми зі скриптом.

Форма зразка:

<form id="my_form" action="/something/todo/create" method="post">
</form>

Маркер автоматично додається до форми у вигляді прихованого поля:

<form id="my_form" action="/something/todo/create" method="post">
<input name="__RequestVerificationToken" type="hidden" value="Cf..." /></form>

І подайте у сценарії:

function DoSomething() {
    $('#my_form').submit();
}

Або використовуючи метод публікації:

function DoSomething() {
    var form = $('#my_form');

    $.post("/something/todo/create", form.serialize());
}

Я думаю, що це рішення працює лише у тому випадку, якщо ваш JavaScript також знаходиться у вашому файлі cshtml.
carlin.scott

6

У 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


Як я можу використовувати форму серіалізувати тут студент: $ ('# frm-student').
Serialize

6

        функція DeletePersonel (id) {

                var data = новий FormData ();
                data.append ("__ RequestVerificationToken", "@ HtmlHelper.GetAntiForgeryToken ()");

                $ .ajax ({
                    тип: "POST",
                    url: '/ Personel / Delete /' + id,
                    дані: дані,
                    кеш: false,
                    dataData: false,
                    contentType: false,
                    успіх: функція (результат) {

                    }
                });

        }
    

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

3

Я знаю, це старе питання. Але я все одно додам свою відповідь, може допомогти комусь, як я.

Якщо ви не хочете обробляти результат від дії пост контролера, наприклад, виклику 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>

3

У контролері облікового запису:

    // 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...


Насправді не практично, значить, ви повинні
кодувати

0

Я спробував багато робочих груп, і не з них працював на мене. Виняток склав "Обов’язкове поле форми підробки" __RequestVerificationToken ".

Що мені допомогло - переключити .ajax на .post:

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

0

Сміливо користуйтесь функцією нижче:

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

}


0

Маркер не працюватиме, якщо його постачав інший контролер. Наприклад, він не працюватиме, якщо подання повернуто Accountsконтролером, а ви POST- Clientsконтролером.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.