include antiforgerytoken in ajax post ASP.NET MVC


168

Sto riscontrando problemi con AntiForgeryToken con ajax. Sto usando ASP.NET MVC 3. Ho provato la soluzione nelle chiamate jQuery Ajax e Html.AntiForgeryToken () . Utilizzando quella soluzione, il token è ora passato:

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

Quando rimuovo l' [ValidateAntiForgeryToken]attributo solo per vedere se i dati (con il token) vengono passati come parametri al controller, posso vedere che vengono passati. Ma per qualche motivo, il A required anti-forgery token was not supplied or was invalid.messaggio appare ancora quando rimetto l'attributo.

Qualche idea?

MODIFICARE

L'antiforgerytoken viene generato all'interno di un modulo, ma non sto usando un'azione di invio per inviarlo. Invece, sto solo ottenendo il valore del token usando jquery e quindi provando a postarlo ajax.

Ecco il modulo che contiene il token e si trova nella pagina principale superiore:

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

Risposte:


289

Hai erroneamente specificato l' contentTypea application/json.

Ecco un esempio di come potrebbe funzionare.

controller:

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

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

Visualizza:

@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>

Ciao, grazie per la rapida risposta. Mi dispiace di non averlo menzionato nella domanda; Al momento non sto utilizzando l'azione di invio. (Il token è in una forma, ma non sto usando un pulsante di invio per inviarlo). È possibile solo cambiare il tipo di contenuto in qualcos'altro?
GU Raqueño,

13
Il fatto che tu non stia utilizzando un'azione di invio non cambia molto la mia risposta. Tutto quello che devi fare è iscriverti a qualche altro evento (un clic sul pulsante, un clic di ancoraggio o qualsiasi altra cosa e semplicemente leggere il valore del campo nascosto). Per quanto riguarda l'invio della richiesta AJAX, è possibile utilizzare l'esempio fornito nella mia risposta. Si consiglia di non utilizzare contentTypeper application/jsonperché il server si aspetta che il __RequestVerificationTokenparametro da parte della richiesta payload POST utilizzando application/x-www-form-urlencoded.
Darin Dimitrov,

come questo codice $(this).data('url'),può capire quale sarebbe l'URL del mio controller e azione. Spiega per favore. grazie
Mou,

2
La domanda originale riguardava contentType: 'application / json'. Per i normali post ajax incluso __RequestVerificationToken nel post del modulo funzionerà ovviamente perché è come un normale post del modulo. Quando vuoi pubblicare json comunque (da qui il tipo di contenuto) questo non sembra funzionare. Quindi questo è un caso di accettazione errata di quanto sopra come risposta.
Giovanni,

Devo usare "ModelState.IsValid"? Come posso dire che funziona?
Moran Monovich,

61

Un altro approccio (meno javascript), che ho fatto, è simile a questo:

Innanzitutto, un helper 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));
}

che restituirà una stringa

__RequestVerificationToken:"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1"

così possiamo usarlo in questo modo

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

E sembra funzionare!


5
+1, carino. Ho appena diviso @Html.AntiForgeryTokenForAjaxPostin due per ottenere il nome del token in una mano e il suo valore nell'altra. Altrimenti l'evidenziazione della sintassi è tutta incasinata. Finisce così (rimosse anche le virgolette singole dal risultato restituito, in modo che si comporti come qualsiasi aiuto MVC, ad esempio @Url):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein

4
niente male. Con questo hai ajax call n file cshtm .... non dovresti mox js con il rasoio così tanto secondo me.
bunny1985,

Ho annullato questa domanda perché ritengo che un approccio più semplice sia utilizzare la classe statica AntiForgery. Ottenere HTML e sostituirlo invece di ottenere direttamente il valore del token è una cattiva pratica. ASP.NET è completamente open source: github.com/ASP-NET-MVC/aspnetwebstack/blob/… (ma ora potrebbe valere la pena scrivere un'altra risposta con un metodo di estensione personalizzato che ottenga solo il token)
usr-local- ΝΩΝ

4
Un modo più pulito di ottenere solo il valore del token sarebbe usare XElement. XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui,

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

45

è così semplice! quando usi@Html.AntiForgeryToken() nel tuo codice html significa che il server ha firmato questa pagina e ogni richiesta inviata al server da questa particolare pagina ha un segno che impedisce agli hacker di inviare una richiesta falsa. quindi per questa pagina per essere autenticata dal server dovresti seguire due passaggi:

1.invia un parametro chiamato __RequestVerificationTokene per ottenere il suo valore usa i codici seguenti:

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

ad esempio prendere una chiamata 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,
});

e il passaggio 2 decora semplicemente il tuo metodo d'azione [ValidateAntiForgeryToken]


Grazie, funziona benissimo per JSON Post ... mi mancava contentType :(
Snziv Gupta

9

In Asp.Net Core è possibile richiedere direttamente il token, come documentato :

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

E usalo in JavaScript:

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

È possibile aggiungere il filtro globale consigliato, come documentato :

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

Aggiornare

La soluzione sopra funziona negli script che fanno parte del file .cshtml. In caso contrario, non è possibile utilizzarlo direttamente. La mia soluzione era quella di utilizzare un campo nascosto per memorizzare prima il valore.

La mia soluzione alternativa, utilizzando ancora GetAntiXsrfRequestToken:

Quando non c'è forma:

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

L' nameattributo può essere omesso poiché utilizzo l' idattributo.

Ogni modulo include questo token. Quindi, invece di aggiungere un'altra copia dello stesso token in un campo nascosto, puoi anche cercare un campo esistente per name. Nota: all'interno di un documento possono essere presenti più moduli, quindi namein tal caso non è univoco. A differenza di un idattributo che dovrebbe essere unico.

Nello script, trova per id:

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

Un'alternativa, senza dover fare riferimento al token, è inviare il modulo con lo script.

Modulo di esempio:

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

Il token viene automaticamente aggiunto al modulo come campo nascosto:

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

E invia nello script:

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

O usando un metodo post:

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

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

Penso che questa soluzione funzioni solo se il tuo javascript è anche nel tuo file cshtml.
carlin.scott

6

In Asp.Net MVC quando si utilizza @Html.AntiForgeryToken()Razor crea un campo di input nascosto con nome __RequestVerificationTokenper memorizzare i token. Se si desidera scrivere un'implementazione AJAX, è necessario recuperare manualmente questo token e passarlo come parametro al server in modo che possa essere convalidato.

Passaggio 1: ottieni il token

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

Passaggio 2: passare il token nella chiamata 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')
      }
})
};

Nota : il tipo di contenuto dovrebbe essere'application/x-www-form-urlencoded; charset=utf-8'

Ho caricato il progetto su Github; puoi scaricare e provarlo.

https://github.com/lambda2016/AjaxValidateAntiForgeryToken


Come posso usare il modulo serializzare qui studente: $ ('# frm-student'). Serialize (),
LittleDragon

6

        funzione DeletePersonel (id) {

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

                $ .Ajax ({
                    tipo: "POST",
                    url: "/ Personel / Delete /" + id,
                    dati: dati,
                    cache: false,
                    processData: false,
                    contentType: false,
                    successo: funzione (risultato) {

                    }
                });

        }
    

        classe statica pubblica HtmlHelper
        {
            stringa statica pubblica GetAntiForgeryToken ()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match (System.Web.Helpers.AntiForgery.GetHtml (). ToString (), "(?: Value = \") (. *) (? : \ ")");
                if (value.Success)
                {
                    valore restituito. Gruppi [1] .Valore;
                }
                ritorno "";
            }
        }

3

So che questa è una vecchia domanda. Ma aggiungerò comunque la mia risposta, potrei aiutare qualcuno come me.

Se non si desidera elaborare il risultato dall'azione post del controller, come chiamare il LoggOffmetodo del Accountscontroller, è possibile eseguire la seguente versione della risposta di @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

Nel controller dell'account:

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

In vista:

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

E 'importante insieme contentTypeal 'application/x-www-form-urlencoded; charset=utf-8'o semplicemente omettere contentTypedall'oggetto ...


non molto pratico, significa che devi codificare tutti i moduli, e se i moduli hanno molti elementi potrebbe essere una seccatura :(
djack109

0

Ho provato molti ambienti di lavoro e nessuno di loro ha funzionato per me. L'eccezione era "Il campo modulo anti-contraffazione richiesto" __RequestVerificationToken ".

Ciò che mi ha aiutato è stato il passaggio da .ajax a .post:

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

0

Sentiti libero di usare la funzione qui sotto:

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

Il token non funzionerà se è stato fornito da un controller diverso. Ad esempio, non funzionerà se la vista è stata restituita dal Accountscontroller, ma tu POSTal Clientscontroller.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.