termasuk antiforgerytoken dalam posting ajax ASP.NET MVC

168

Saya mengalami masalah dengan AntiForgeryToken dengan ajax. Saya menggunakan ASP.NET MVC 3. Saya mencoba solusi di panggilan jQuery Ajax dan Html.AntiForgeryToken () . Menggunakan solusi itu, token sekarang sedang diteruskan:

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

Ketika saya menghapus [ValidateAntiForgeryToken]atribut hanya untuk melihat apakah data (dengan token) dilewatkan sebagai parameter ke controller, saya bisa melihat bahwa mereka sedang dilewatkan. Tetapi untuk beberapa alasan, A required anti-forgery token was not supplied or was invalid.pesan itu masih muncul ketika saya mengembalikan atribut.

Ada ide?

EDIT

Antiforgerytoken sedang dibuat di dalam formulir, tapi saya tidak menggunakan tindakan kirim untuk mengirimkannya. Sebagai gantinya, saya hanya mendapatkan nilai token menggunakan jquery dan kemudian mencoba untuk posting ajax itu.

Ini adalah formulir yang berisi token, dan terletak di halaman utama master:

<form id="__AjaxAntiForgeryForm" action="#" method="post">
    @Html.AntiForgeryToken()
</form>
OJ Raqueño
sumber

Jawaban:

289

Anda salah menentukan contentTypeuntuk application/json.

Berikut ini contoh cara kerjanya.

Pengendali:

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

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

Melihat:

@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>
Darin Dimitrov
sumber
Hai, terima kasih atas balasan cepatnya. Maaf saya tidak menyebutkannya dalam pertanyaan; Saya tidak menggunakan tindakan kirim saat ini. (Token dalam formulir, tapi saya tidak menggunakan tombol kirim untuk mengirimkannya). Mungkinkah hanya mengubah jenis konten menjadi sesuatu yang lain?
OJ Raqueño
13
Fakta bahwa Anda tidak menggunakan tindakan kirim tidak banyak mengubah jawaban saya. Yang perlu Anda lakukan adalah berlangganan ke acara lain (klik tombol, klik jangkar atau apa pun dan cukup baca nilai bidang yang disembunyikan). Sejauh mengirim permintaan AJAX yang bersangkutan, Anda dapat menggunakan contoh yang diberikan dalam jawaban saya. Anda tidak harus menggunakan contentTypeuntuk application/jsonkarena server mengharapkan __RequestVerificationTokenparameter untuk menjadi bagian dari POST permintaan payload menggunakan application/x-www-form-urlencoded.
Darin Dimitrov
bagaimana kode ini $(this).data('url'),dapat memahami apa yang akan menjadi url controller dan action saya. tolong jelaskan. terima kasih
Mou
2
Pertanyaan aslinya adalah tentang contentType: 'application / json'. Untuk postingan ajax reguler termasuk __RequestVerificationToken dalam form post jelas akan berfungsi karena seperti post form biasa. Namun ketika Anda ingin memposting json (maka jenis konten) ini sepertinya tidak berfungsi. Jadi ini adalah kasus salah menerima jawaban di atas.
John
Apakah saya perlu menggunakan "ModelState.IsValid"? Bagaimana saya bisa tahu bahwa ini berfungsi?
Moran Monovich
61

Pendekatan lain (kurang javascript) yang saya lakukan, berlangsung seperti ini:

Pertama, pembantu 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));
}

itu akan mengembalikan sebuah string

__RequestVerificationToken:"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1"

jadi kita bisa menggunakannya seperti ini

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

Dan sepertinya berhasil!

Maks
sumber
5
+1, bagus. Saya hanya membaginya menjadi @Html.AntiForgeryTokenForAjaxPostdua untuk mendapatkan nama token di satu tangan dan nilainya di tangan lain. Kalau tidak, highlight sintaksis semuanya kacau. Itu berakhir seperti ini (menghapus tanda kutip tunggal dari hasil yang dikembalikan juga, sehingga berperilaku seperti pembantu MVC, misalnya @Url):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein
4
nit bagus. Dengan ini, Anda memiliki ajax panggilan n file cshtm .... Anda tidak harus memangkas js dengan pisau cukur yang banyak menurut saya.
bunny1985
Saya telah menurunkan pertanyaan ini karena saya percaya bahwa pendekatan yang lebih sederhana adalah dengan menggunakan kelas statis AntiForgery. Mendapatkan HTML dan menggantinya daripada langsung mendapatkan nilai token adalah praktik yang buruk. ASP.NET sepenuhnya open source: github.com/ASP-NET-MVC/aspnetwebstack/blob/… (tapi sekarang mungkin layak untuk menulis jawaban lain dengan metode ekstensi khusus yang hanya mendapatkan token)
usr-local- ΕΨΗΕΛΩΝ
4
Cara yang lebih bersih untuk mendapatkan nilai token adalah menggunakan XElement. XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui
3
@transformervar antiForgeryInputTag = helper.AntiForgeryToken().ToString(); return XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui
45

itu sangat sederhana! ketika Anda menggunakan @Html.AntiForgeryToken()kode html Anda itu berarti server telah menandatangani halaman ini dan setiap permintaan yang dikirim ke server dari halaman tertentu ini memiliki tanda yang dicegah untuk mengirim permintaan palsu oleh peretas. jadi agar halaman ini diautentikasi oleh server Anda harus melalui dua langkah:

1. kirim parameter bernama __RequestVerificationTokendan untuk mendapatkan nilainya gunakan kode di bawah ini:

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

misalnya menerima panggilan 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,
});

dan langkah 2 hiasi metode tindakan Anda dengan [ValidateAntiForgeryToken]

Abolfazl
sumber
Terima kasih, bekerja sangat baik untuk posting json ... saya kehilangan contentType :(
Snziv Gupta
9

Di Asp.Net Core Anda dapat meminta token secara langsung, seperti yang didokumentasikan :

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

Dan gunakan dalam javascript:

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

Anda dapat menambahkan filter global yang direkomendasikan, seperti yang didokumentasikan :

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

Memperbarui

Solusi di atas berfungsi dalam skrip yang merupakan bagian dari .cshtml. Jika ini bukan masalahnya maka Anda tidak dapat menggunakan ini secara langsung. Solusi saya adalah menggunakan bidang tersembunyi untuk menyimpan nilai terlebih dahulu.

Solusi saya, masih menggunakan GetAntiXsrfRequestToken:

Ketika tidak ada formulir:

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

The nameatribut dapat dihilangkan karena saya menggunakan idatribut.

Setiap formulir menyertakan token ini. Jadi, alih-alih menambahkan salinan lain dari token yang sama di bidang tersembunyi, Anda juga dapat mencari bidang yang sudah ada name. Harap dicatat: mungkin ada beberapa bentuk di dalam dokumen, jadi namedalam hal ini tidak unik. Berbeda dengan idatribut yang harus unik.

Dalam skrip, temukan oleh id:

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

Alternatif, tanpa harus merujuk token, adalah menyerahkan formulir dengan skrip.

Bentuk sampel:

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

Token ditambahkan secara otomatis ke formulir sebagai bidang tersembunyi:

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

Dan kirimkan dalam skrip:

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

Atau menggunakan metode posting:

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

    $.post("/something/todo/create", form.serialize());
}
Ruard van Elburg
sumber
Saya pikir solusi ini hanya berfungsi jika javascript Anda juga ada di file cshtml Anda.
carlin.scott
6

Di Asp.Net MVC saat Anda menggunakan @Html.AntiForgeryToken()Razor membuat bidang input tersembunyi dengan nama __RequestVerificationTokenuntuk menyimpan token. Jika Anda ingin menulis implementasi AJAX, Anda harus mengambil token ini sendiri dan meneruskannya sebagai parameter ke server sehingga dapat divalidasi.

Langkah 1: Dapatkan token

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

Langkah 2: Lulus token di panggilan 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')
      }
})
};

Catatan : Jenis konten harus'application/x-www-form-urlencoded; charset=utf-8'

Saya telah mengunggah proyek di Github; Anda dapat mengunduh dan mencobanya.

https://github.com/lambda2016/AjaxValidateAntiForgeryToken

Frank Odoom
sumber
Bagaimana saya bisa menggunakan form serialize here student: $ ('# frm-student'). Serialize (),
LittleDragon
6

        function DeletePersonel (id) {

                var data = FormData baru ();
                data.append ("__ RequestVerificationToken", "@ HtmlHelper.GetAntiForgeryToken ()");

                $ .ajax ({
                    ketik: 'POST',
                    url: '/ Personel / Hapus /' + id,
                    data: data,
                    cache: false,
                    processData: false,
                    contentType: false,
                    sukses: fungsi (hasil) {

                    }
                });

        }
    

        HtmlHelper public static kelas
        {
            string statis publik GetAntiForgeryToken ()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match (System.Web.Helpers.AntiForgery.GetHtml (). ToString (), "(?: Value = \") (. * *) (? : \ ")");
                jika (nilai. Berhasil)
                {
                    mengembalikan nilai.Kelompok [1] .Nilai;
                }
                kembali "";
            }
        }
ismail eski
sumber
3

Saya tahu ini adalah pertanyaan lama. Tapi saya akan menambahkan jawaban saya, mungkin bisa membantu orang seperti saya.

Jika Anda tidak ingin memproses hasil dari tindakan posting controller, seperti memanggil LoggOffmetode Accountscontroller, Anda dapat melakukan sebagai versi berikut dari jawaban @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>
Aamir
sumber
3

Dalam Pengontrol akun:

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

Dalam penglihatan:

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

Hal ini penting untuk set contentTypeke 'application/x-www-form-urlencoded; charset=utf-8'atau hanya menghilangkan contentTypedari objek ...

Adel Mourad
sumber
tidak benar-benar praktis, berarti Anda harus membuat kode setiap formulir, dan jika formulir memiliki banyak elemen, itu bisa jadi
menyusahkan
0

Saya mencoba banyak workarrounds dan tidak ada yang berhasil untuk saya. Pengecualian adalah "Bidang formulir anti-pemalsuan yang diperlukan" __RequestVerificationToken ".

Apa yang membantu saya adalah mengganti formulir .ajax ke .post:

$.post(
    url,
    $(formId).serialize(),
    function (data) {
        $(formId).html(data);
    });
Stefan Michev
sumber
0

Silakan menggunakan fungsi di bawah ini:

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

}

Komal Narang
sumber
0

Token tidak akan berfungsi jika dipasok oleh pengontrol yang berbeda. Misalnya tidak akan berfungsi jika tampilan dikembalikan oleh Accountscontroller, tetapi Anda POSTke Clientscontroller.

Tagihan Luar Biasa
sumber