Fügen Sie Antiforgerytoken in Ajax nach ASP.NET MVC ein

167

Ich habe Probleme mit dem AntiForgeryToken mit Ajax. Ich verwende ASP.NET MVC 3. Ich habe die Lösung in jQuery Ajax-Aufrufen und im Html.AntiForgeryToken () ausprobiert . Mit dieser Lösung wird das Token jetzt übergeben:

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

Wenn ich das [ValidateAntiForgeryToken]Attribut entferne , um zu sehen, ob die Daten (mit dem Token) als Parameter an die Steuerung übergeben werden, kann ich sehen, dass sie übergeben werden. Aber aus irgendeinem Grund wird die A required anti-forgery token was not supplied or was invalid.Nachricht immer noch angezeigt, wenn ich das Attribut zurücksetze.

Irgendwelche Ideen?

BEARBEITEN

Das Antiforgerytoken wird in einem Formular generiert, aber ich verwende keine Übermittlungsaktion, um es abzusenden. Stattdessen erhalte ich nur den Wert des Tokens mit jquery und versuche dann, ihn zu posten.

Hier ist das Formular, das das Token enthält und sich auf der oberen Masterseite befindet:

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

Antworten:

288

Sie haben das contentTypeto falsch angegeben application/json.

Hier ist ein Beispiel, wie dies funktionieren könnte.

Regler:

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

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

Aussicht:

@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
quelle
Hallo, danke für die schnelle Antwort. Entschuldigung, ich habe es in der Frage nicht erwähnt. Ich verwende die Submit-Aktion derzeit nicht. (Das Token liegt in einer Form vor, aber ich verwende keine Schaltfläche zum Senden, um es zu senden.) Ist es möglich, nur den Inhaltstyp in etwas anderes zu ändern?
ABl. Raqueño
13
Die Tatsache, dass Sie keine Übermittlungsaktion verwenden, ändert an meiner Antwort nicht viel. Sie müssen lediglich ein anderes Ereignis abonnieren (einen Klick auf eine Schaltfläche, einen Ankerklick oder was auch immer und einfach den Wert für das ausgeblendete Feld lesen). Für das Senden der AJAX-Anfrage können Sie das in meiner Antwort angegebene Beispiel verwenden. Sie sollten dies nicht verwenden contentType, application/jsonda der Server erwartet, dass der __RequestVerificationTokenParameter Teil der POST-Anforderungsnutzlast ist, die verwendet wird application/x-www-form-urlencoded.
Darin Dimitrov
Wie kann dieser Code $(this).data('url'),verstehen, wie die URL meines Controllers und meiner Aktion lauten würde? bitte erkläre. danke
Mou
2
Die ursprüngliche Frage betraf contentType: 'application / json'. Bei regulären Ajax-Posts, einschließlich des __RequestVerificationToken im Formularbeitrag, funktioniert dies offensichtlich, da es sich um einen regulären Formularbeitrag handelt. Wenn Sie jedoch json posten möchten (daher der Inhaltstyp), scheint dies nicht zu funktionieren. Dies ist also ein Fall, in dem das oben Gesagte fälschlicherweise als Antwort akzeptiert wird.
John
Muss ich "ModelState.IsValid" verwenden? Wie kann ich feststellen, dass dies funktioniert?
Moran Monovich
61

Ein anderer (weniger javascriptischer) Ansatz, den ich gemacht habe, geht ungefähr so:

Zuerst ein HTML-Helfer

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

das wird eine Zeichenfolge zurückgeben

__RequestVerificationToken:"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1"

so können wir es so verwenden

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

Und es scheint zu funktionieren!

Max
quelle
5
+1, schön. Ich habe das nur @Html.AntiForgeryTokenForAjaxPostin zwei Teile geteilt , um den Token-Namen in der einen und den Wert in der anderen Hand zu erhalten. Andernfalls ist das Syntax-Highlight völlig durcheinander. Es endet so (entfernte auch die einfachen Anführungszeichen aus dem zurückgegebenen Ergebnis, so dass es sich wie jeder MVC-Helfer verhält, zum Beispiel @Url):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein
4
nit nett. Mit diesem haben Sie Ajax Call n CSHTM-Datei .... Sie sollten Js meiner Meinung nach nicht so viel mit Rasiermesser moxen.
Bunny1985
Ich habe diese Frage abgelehnt, weil ich glaube, dass ein einfacherer Ansatz darin besteht, die statische Klasse AntiForgery zu verwenden. Es ist eine schlechte Praxis, HTML abzurufen und zu ersetzen, anstatt den Token-Wert direkt abzurufen. ASP.NET ist vollständig Open Source: github.com/ASP-NET-MVC/aspnetwebstack/blob/… (aber jetzt könnte es sich lohnen, eine weitere Antwort mit einer benutzerdefinierten Erweiterungsmethode zu schreiben, die nur das Token erhält)
usr-local- ΕΨΗΕΛΩΝ
4
Eine sauberere Möglichkeit, nur den Token-Wert zu erhalten, wäre die Verwendung von XElement. XElement.Parse(antiForgeryInputTag).Attribute("value").Value
Darrunategui
3
@transformervar antiForgeryInputTag = helper.AntiForgeryToken().ToString(); return XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui
45

es ist so einfach! Wenn Sie @Html.AntiForgeryToken()in Ihrem HTML-Code verwenden, bedeutet dies, dass der Server diese Seite signiert hat und jede Anfrage, die von dieser bestimmten Seite an den Server gesendet wird, ein Zeichen hat, das verhindert, dass Hacker eine gefälschte Anfrage senden. Damit diese Seite vom Server authentifiziert wird, müssen Sie zwei Schritte ausführen:

1.Senden Sie einen Parameter mit dem Namen __RequestVerificationTokenund verwenden Sie die folgenden Codes, um seinen Wert abzurufen :

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

Nehmen Sie zum Beispiel einen Ajax-Anruf entgegen

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

und Schritt 2 dekorieren Sie einfach Ihre Aktionsmethode mit [ValidateAntiForgeryToken]

Abolfazl
quelle
Danke, funktioniert großartig für json post ...
mir
9

In Asp.Net Core können Sie das Token direkt anfordern, wie dokumentiert :

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

Und verwenden Sie es in Javascript:

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

Sie können den empfohlenen globalen Filter wie folgt hinzufügen :

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

Aktualisieren

Die obige Lösung funktioniert in Skripten, die Teil der .cshtml sind. Wenn dies nicht der Fall ist, können Sie dies nicht direkt verwenden. Meine Lösung bestand darin, ein verstecktes Feld zu verwenden, um den Wert zuerst zu speichern.

Meine Problemumgehung verwendet immer noch GetAntiXsrfRequestToken:

Wenn es keine Form gibt:

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

Das nameAttribut kann weggelassen werden, da ich das idAttribut verwende.

Jedes Formular enthält dieses Token. Anstatt also eine weitere Kopie desselben Tokens in ein verstecktes Feld einzufügen, können Sie auch nach einem vorhandenen Feld suchen name. Bitte beachten Sie: Ein Dokument kann mehrere Formulare enthalten und nameist in diesem Fall nicht eindeutig. Im Gegensatz zu einem idAttribut , das sollte eindeutig sein.

Suchen Sie im Skript nach ID:

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

Eine Alternative, ohne auf das Token verweisen zu müssen, besteht darin, das Formular mit einem Skript zu senden.

Musterformular:

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

Das Token wird dem Formular automatisch als verstecktes Feld hinzugefügt:

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

Und im Skript einreichen:

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

Oder mit einer Post-Methode:

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

    $.post("/something/todo/create", form.serialize());
}
Ruard van Elburg
quelle
Ich denke, diese Lösung funktioniert nur, wenn sich Ihr Javascript auch in Ihrer cshtml-Datei befindet.
carlin.scott
6

In Asp.Net MVC wird bei Verwendung von @Html.AntiForgeryToken()Razor ein verstecktes Eingabefeld mit Namen __RequestVerificationTokenzum Speichern von Token erstellt. Wenn Sie eine AJAX-Implementierung schreiben möchten, müssen Sie dieses Token selbst abrufen und als Parameter an den Server übergeben, damit es validiert werden kann.

Schritt 1: Holen Sie sich den Token

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

Schritt 2: Übergeben Sie das Token im AJAX-Aufruf

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

Hinweis : Der Inhaltstyp sollte sein'application/x-www-form-urlencoded; charset=utf-8'

Ich habe das Projekt auf Github hochgeladen. Sie können es herunterladen und ausprobieren.

https://github.com/lambda2016/AjaxValidateAntiForgeryToken

Frank Odoom
quelle
Wie kann ich Form serialize hier verwenden student: $ ('# frm-student'). Serialize (),
LittleDragon
6

        Funktion DeletePersonel (id) {

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

                $ .ajax ({
                    Typ: 'POST',
                    URL: '/ Personel / Löschen /' + ID,
                    Daten: Daten,
                    Cache: false,
                    processData: false,
                    contentType: false,
                    Erfolg: Funktion (Ergebnis) {

                    }}
                });

        }}
    

        öffentliche statische Klasse HtmlHelper
        {
            öffentliche statische Zeichenfolge GetAntiForgeryToken ()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match (System.Web.Helpers.AntiForgery.GetHtml (). ToString (), "(?: Value = \") (. *) (? : ")");
                if (value.Success)
                {
                    Rückgabewert.Groups [1] .Value;
                }}
                Rückkehr "";
            }}
        }}
ismail eski
quelle
3

Ich weiß, dass dies eine alte Frage ist. Aber ich werde trotzdem meine Antwort hinzufügen, könnte jemandem wie mir helfen.

Wenn Sie das Ergebnis der Post-Aktion LoggOffdes AccountsControllers nicht verarbeiten möchten, wie z. B. das Aufrufen der Controller- Methode , können Sie die folgende Version der Antwort von @DarinDimitrov wie folgt ausführen:

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

Im Account Controller:

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

Im Hinblick auf:

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

Es ist wichtig zu Satz contentTypeauf 'application/x-www-form-urlencoded; charset=utf-8'oder weglassen contentTypevon dem Objekt ...

Adel Mourad
quelle
Nicht wirklich praktisch, bedeutet, dass Sie jedes Formular codieren müssen. Wenn Formulare viele Elemente enthalten, kann dies schmerzhaft sein :(
djack109
0

Ich habe viele Workarrounds ausprobiert und keiner von ihnen hat für mich funktioniert. Die Ausnahme war "Das erforderliche Feld für Fälschungsschutz" __RequestVerificationToken ".

Was mir geholfen hat, war, von .ajax zu .post zu wechseln:

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

Fühlen Sie sich frei, die folgende Funktion zu nutzen:

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
quelle
0

Das Token funktioniert nicht, wenn es von einem anderen Controller bereitgestellt wurde. ZB funktioniert es nicht, wenn die Ansicht vom AccountsController zurückgegeben wurde, aber Sie POSTzum ClientsController.

Ausstehende Rechnung
quelle