รวม antiforgerytoken ใน Ajax post ASP.NET MVC


168

ฉันมีปัญหากับ AntiForgeryToken ด้วย ajax ฉันใช้ 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.ข้อความยังคงปรากฏขึ้นเมื่อฉันใส่คุณลักษณะกลับ

ความคิดใด ๆ

แก้ไข

antiforgerytoken กำลังถูกสร้างขึ้นภายในแบบฟอร์ม แต่ฉันไม่ได้ใช้การส่งเพื่อส่ง แต่ฉันแค่รับค่าโทเค็นโดยใช้ jQuery แล้วลอง ajax โพสต์นั้น

นี่คือฟอร์มที่มีโทเค็นและอยู่ที่หน้าต้นแบบด้านบน:

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

คำตอบ:


289

คุณได้ระบุไม่ถูกต้องไปcontentTypeapplication/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' สำหรับโพสต์อาแจ็กซ์ปกติรวมถึง __RequestVerificationToken ในโพสต์แบบฟอร์มจะทำงานได้อย่างชัดเจนเพราะมันเป็นเหมือนโพสต์รูปแบบปกติ เมื่อคุณต้องการโพสต์ json อย่างไรก็ตาม (ประเภทเนื้อหา) สิ่งนี้ดูเหมือนจะไม่ทำงาน ดังนั้นนี่เป็นกรณีของการยอมรับคำตอบข้างต้นอย่างไม่ถูกต้อง
John

ฉันต้องใช้ "ModelState.IsValid" หรือไม่ ฉันจะบอกได้อย่างไรว่านี่ใช้งานได้?
Moran Monovich

61

อีกวิธีหนึ่งที่ฉันทำไปแบบนี้:

ก่อนอื่นคือผู้ช่วย 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 .... คุณไม่ควร mox js ด้วยมีดโกนที่มีมากในความคิดของฉัน
bunny1985

ฉันได้ลงคะแนนคำถามนี้เพราะฉันเชื่อว่าวิธีที่ง่ายกว่าคือการใช้ 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());
}

ฉันคิดว่าวิธีนี้ใช้ได้ผลถ้าจาวาสคริปต์ของคุณอยู่ในไฟล์ 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') ทำให้เป็นอันดับ (),
LittleDragon

6

        ฟังก์ชัน DeletePersonel (id) {

                ข้อมูล var = FormData ใหม่ ();
                data.append ("__ RequestVerificationToken", "@ HtmlHelper.GetAntiForgeryToken ()");

                $ .ajax ({
                    ประเภท: 'POST'
                    url: '/ บุคลากร / ลบ /' + id
                    ข้อมูล: ข้อมูล
                    แคช: เท็จ
                    processData: false
                    contentType: เท็จ
                    ความสำเร็จ: ฟังก์ชัน (ผลลัพธ์) {

                    }
                });

        }
    

        HtmlHelper คลาสคงที่สาธารณะ
        {
            ประชาชนคงที่สตริง GetAntiForgeryToken ()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match (System.Web.Helpers.AntiForgery.GetHtml (). ToString (), "(?: value = \") (. * : \ ")");
                ถ้า (ค่าสำเร็จ)
                {
                    ส่งคืนค่ากลุ่ม [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จากวัตถุ ...


ไม่สามารถใช้งานได้จริงหมายความว่าคุณต้องเขียนโค้ดทุกรูปแบบและถ้าแบบฟอร์มมีองค์ประกอบจำนวนมากอาจเป็นความเจ็บปวด :(
djack109

0

ฉันพยายามแก้ไขปัญหาหลายอย่างและไม่ทำงานให้ฉัน ข้อยกเว้นคือ "เขตข้อมูลฟอร์มการป้องกันการปลอมแปลงที่จำเป็น" __RequestVerificationToken "

สิ่งที่ช่วยฉันได้คือเปลี่ยนรูปแบบ. jax เป็น. 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คอนโทรลเลอร์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.