ajax post ASP.NET MVCに偽造防止トークンを含める


168

ajaxでAntiForgeryTokenに問題があります。私はASP.NET MVC 3を使用しています。jQueryAjax呼び出しと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.、属性を元に戻すとメッセージがポップアップします。

何か案は?

編集

偽造防止トークンがフォーム内で生成されていますが、送信アクションを使用してフォームを送信していません。代わりに、jqueryを使用してトークンの値を取得し、それをajax投稿しようとしています。

以下は、トークンを含むフォームで、トップマスターページにあります。

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

回答:


289

toを誤って指定し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>

こんにちは、迅速な返信ありがとうございます。申し訳ありませんでしたが、質問では触れませんでした。現在、submitアクションは使用していません。(トークンはフォームにありますが、送信ボタンを使用して送信していません)。コンテンツタイプを別のタイプに変更することは可能ですか?
OJRaqueño2013年

13
送信アクションを使用していないという事実は、私の答えをあまり変えません。必要なのは、他のイベント(ボタンクリック、アンカークリックなど)をサブスクライブするだけで、非表示フィールドの値を読み取るだけです。AJAXリクエストの送信に関する限り、私の回答で提供されている例を使用できます。サーバーはを使用してパラメータがPOSTリクエストのペイロードの一部であることを想定しているため、使用contentTypeしないでください。application/json__RequestVerificationTokenapplication/x-www-form-urlencoded
Darin Dimitrov 2013年

このコード$(this).data('url'),がコントローラーとアクションのURLを理解する方法。説明してください。感謝
Mou

2
元の質問はcontentType: 'application / json'に関するものでした。フォームポストに__RequestVerificationTokenを含む通常のajaxポストは、通常のフォームポストのように機能するため、明らかに機能します。ただし、jsonを投稿する場合(したがってコンテンツタイプ)、これは機能しないようです。したがって、これは上記を回答として誤って受け入れる場合です。
John

「ModelState.IsValid」を使用する必要がありますか?これが機能していることをどうやって確認できますか?
Moran Monovich 2017

61

私が行った別の(JavaScriptを使わない)アプローチは次のようなものです。

まず、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トークン名を一方の手に、その値をもう一方の手に入れるために、私は2つに分けます。そうしないと、構文の強調表示がすべて乱れます。最終的には次のようになります(返された結果から単一引用符も削除されたため、たとえば@UrlなどのMVCヘルパーのように動作します):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein

4
素敵なニット。これであなたはajax call n cshtmファイルを持っています...あなたは私の意見ではそれほどかみそりでjsをmoxするべきではありません。
bunny1985

より単純なアプローチはAntiForgery静的クラスを使用することだと私は信じているので、私はこの質問に反対票を投じました。トークン値を直接取得するのではなく、HTMLを取得して置き換えることは悪い習慣です。ASP.NETは完全にオープンソースです:github.com/ASP-NET-MVC/aspnetwebstack/blob/…(ただし、トークンのみを取得するカスタム拡張メソッドを使用して別の回答を書く価値があるかもしれません)
usr-local- ΕΨΗΕΛΩΝ15年

4
トークンの値だけを取得するよりクリーンな方法は、XElementを使用することです。XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui 2017年

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

45

とても簡単です!@Html.AntiForgeryToken()HTMLコードで使用する場合、サーバーがこのページに署名しており、この特定のページからサーバーに送信される各リクエストには、ハッカーによる偽のリクエストの送信が禁止されているサインがあることを意味します。したがって、このページがサーバーによって認証されるためには、次の2つのステップを実行する必要があります。

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が19:18

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


ここで生徒のフォームをシリアライズする方法を
教えてください

6

        function DeletePersonel(id){

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

                $ .ajax({
                    タイプ: 'POST'、
                    url: '/ Personel / Delete /' + id、
                    データ:データ、
                    キャッシュ:false、
                    processData:false、
                    contentType:false、
                    成功:関数(結果){

                    }
                });

        }
    

        パブリック静的クラスHtmlHelper
        {
            パブリック静的文字列GetAntiForgeryToken()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml()。ToString()、 "(?:value = \")(。*)(? :\ ")");
                if(value.Success)
                {
                    戻り値.Groups [1] .Value;
                }
                ""を返す;
            }
        }

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あなたは、コントローラが、POSTClientsコントローラ。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.