bao gồm antiforgerytoken trong bài ajax ASP.NET MVC


167

Tôi gặp sự cố với AntiForgeryToken với ajax. Tôi đang sử dụng ASP.NET MVC 3. Tôi đã thử giải pháp trong các cuộc gọi jQuery Ajax và Html.AntiForgeryToken () . Sử dụng giải pháp đó, mã thông báo hiện đang được thông qua:

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

Khi tôi xóa [ValidateAntiForgeryToken]thuộc tính chỉ để xem liệu dữ liệu (có mã thông báo) có được truyền dưới dạng tham số cho bộ điều khiển hay không, tôi có thể thấy rằng chúng đang được truyền. Nhưng vì một số lý do, A required anti-forgery token was not supplied or was invalid.thông báo vẫn bật lên khi tôi đặt lại thuộc tính.

Có ý kiến ​​gì không?

BIÊN TẬP

Antiforgerytoken đang được tạo trong một biểu mẫu, nhưng tôi không sử dụng hành động gửi để gửi nó. Thay vào đó, tôi chỉ nhận được giá trị của mã thông báo bằng cách sử dụng jquery và sau đó cố gắng đăng bài đó.

Đây là biểu mẫu có chứa mã thông báo và được đặt ở trang chính trên cùng:

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

Câu trả lời:


288

Bạn đã chỉ định không chính xác contentTypeđến application/json.

Đây là một ví dụ về cách thức này có thể hoạt động.

Điều khiển:

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

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

Lượt xem:

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

Chào, cảm ơn vì đã phản hồi nhanh chóng. Xin lỗi tôi đã không đề cập đến nó trong câu hỏi; Tôi hiện không sử dụng hành động gửi. (Mã thông báo ở dạng, nhưng tôi không sử dụng nút gửi để gửi). Có thể chỉ cần thay đổi loại nội dung thành một cái gì đó khác?
OJ Raqueño

13
Việc bạn không sử dụng hành động gửi sẽ không thay đổi câu trả lời của tôi nhiều. Tất cả những gì bạn cần làm là đăng ký một số sự kiện khác (nhấp chuột, nhấp chuột neo hoặc bất cứ điều gì và chỉ cần đọc giá trị trường ẩn). Khi gửi yêu cầu AJAX, bạn có thể sử dụng ví dụ được cung cấp trong câu trả lời của tôi. Bạn không nên sử dụng contentTypeđể application/jsonvì máy chủ được mong đợi các __RequestVerificationTokentham số là một phần của yêu cầu tải trọng POST sử dụng application/x-www-form-urlencoded.
Darin Dimitrov

làm thế nào mã này $(this).data('url'),có thể hiểu những gì sẽ là url của bộ điều khiển và hành động của tôi. vui lòng giải thích. cảm ơn
Mou

2
Câu hỏi ban đầu là về contentType: 'application / json'. Đối với các bài đăng ajax thông thường bao gồm __RequestVerifyingToken trong bài đăng mẫu rõ ràng sẽ hoạt động vì nó giống như một bài đăng mẫu thông thường. Tuy nhiên, khi bạn muốn đăng json (do đó loại nội dung) thì điều này dường như không hoạt động. Vì vậy, đây là một trường hợp chấp nhận không chính xác như trên là một câu trả lời.
Giăng

Tôi có cần sử dụng "ModelState.IsValid" không? Làm thế nào tôi có thể nói rằng điều này đang làm việc?
Moran Monovich

61

Một cách tiếp cận khác (ít javascript), mà tôi đã làm, đi một cái gì đó như thế này:

Đầu tiên, một người trợ giúp 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));
}

điều đó sẽ trả về một chuỗi

__RequestVerificationToken:"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1"

vì vậy chúng ta có thể sử dụng nó như thế này

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

Và nó dường như làm việc!


5
+1, tốt đẹp. Tôi chỉ chia @Html.AntiForgeryTokenForAjaxPosthai phần để lấy tên mã thông báo bằng một tay và giá trị của nó trong tay kia. Nếu không, cú pháp tô sáng là tất cả sai lầm. Nó kết thúc như thế này (cũng loại bỏ các trích dẫn đơn từ kết quả trả về, để nó hoạt động như bất kỳ trình trợ giúp MVC nào, ví dụ @Url):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein 18/03/14

4
nit đẹp. Với điều này Bạn có ajax gọi n tập tin cshtm .... bạn không nên mox js với dao cạo nhiều như vậy theo ý kiến ​​của tôi.
bunny1985

Tôi đã đánh giá thấp câu hỏi này bởi vì tôi tin rằng một cách tiếp cận đơn giản hơn là sử dụng lớp tĩnh AntiForgery. Nhận HTML và thay thế nó thay vì trực tiếp nhận giá trị mã thông báo là thực tế xấu. ASP.NET là nguồn mở hoàn toàn: github.com/ASP-NET-MVC/aspnetwebstack/blob/ mẹo (nhưng bây giờ có thể đáng để viết một câu trả lời khác với phương thức tiện ích mở rộng tùy chỉnh chỉ nhận được mã thông báo)
usr-local- ΕΨΗΕΛΩΝ

4
Một cách sạch hơn để chỉ nhận giá trị mã thông báo sẽ là sử dụng XEuity. XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunargetui

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

45

nó thật đơn giản Khi bạn sử dụng @Html.AntiForgeryToken()mã html của mình, điều đó có nghĩa là máy chủ đã ký trang này và mỗi yêu cầu được gửi đến máy chủ từ trang cụ thể này có một dấu hiệu bị ngăn chặn gửi yêu cầu giả mạo của tin tặc. Vì vậy, để trang này được xác thực bởi máy chủ, bạn nên trải qua hai bước:

1. gửi một tham số có tên __RequestVerificationTokenvà nhận mã sử dụng giá trị bên dưới:

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

ví dụ như thực hiện cuộc gọi 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,
});

và bước 2 chỉ cần trang trí phương thức hành động của bạn bằng cách [ValidateAntiForgeryToken]


Cảm ơn, hoạt động tuyệt vời cho bài đăng json ... tôi đã bị mất
nội dungType

9

Trong Asp.Net Core, bạn có thể yêu cầu mã thông báo trực tiếp, như được ghi lại :

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

Và sử dụng nó trong javascript:

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

Bạn có thể thêm bộ lọc toàn cầu được đề xuất, như được ghi lại :

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

Cập nhật

Giải pháp trên hoạt động trong các tập lệnh là một phần của .cshtml. Nếu đây không phải là trường hợp thì bạn không thể sử dụng trực tiếp. Giải pháp của tôi là sử dụng một trường ẩn để lưu trữ giá trị đầu tiên.

Cách giải quyết của tôi, vẫn đang sử dụng GetAntiXsrfRequestToken:

Khi không có hình thức:

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

Các namethuộc tính có thể được bỏ qua kể từ khi tôi sử dụng các idthuộc tính.

Mỗi hình thức bao gồm mã thông báo này. Vì vậy, thay vì thêm một bản sao khác của cùng một mã thông báo vào một trường ẩn, bạn cũng có thể tìm kiếm một trường hiện có bằng cách name. Xin lưu ý: có thể có nhiều biểu mẫu trong một tài liệu, vì vậy nametrong trường hợp đó không phải là duy nhất. Không giống như một idthuộc tính nên là duy nhất.

Trong tập lệnh, tìm theo id:

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

Một cách khác, mà không phải tham chiếu mã thông báo, là gửi biểu mẫu với tập lệnh.

Mẫu mẫu:

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

Mã thông báo được tự động thêm vào biểu mẫu dưới dạng trường ẩn:

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

Và gửi trong kịch bản:

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

Hoặc sử dụng một phương pháp bài:

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

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

Tôi nghĩ giải pháp này chỉ hoạt động nếu javascript của bạn cũng nằm trong tệp cshtml của bạn.
carlin.scott

6

Trong Asp.Net MVC khi bạn sử dụng @Html.AntiForgeryToken()Dao cạo tạo trường nhập liệu ẩn có tên__RequestVerificationToken để lưu trữ mã thông báo. Nếu bạn muốn viết một triển khai AJAX, bạn phải tự tìm nạp mã thông báo này và chuyển nó dưới dạng tham số cho máy chủ để có thể xác thực nó.

Bước 1: Nhận mã thông báo

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

Bước 2: Truyền mã thông báo trong cuộc gọi 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')
      }
})
};

Ghi chú : Loại nội dung phải là'application/x-www-form-urlencoded; charset=utf-8'

Tôi đã tải lên dự án trên Github; bạn có thể tải về và dùng thử.

https://github.com/lambda2016/AjaxValidateAntiForgeryToken


Làm thế nào tôi có thể sử dụng hình thức sắp đặt ở đây học sinh:. $ ( '# Frm-sinh viên') serialize (),
LittleDragon

6

        chức năng DeletePersonel (id) {

                dữ liệu var = FormData mới ();
                data.append ("__ RequestVerifyingToken", "@ HtmlHelper.GetAntiForgeryToken ()");

                $ .ajax ({
                    gõ: 'POST',
                    url: '/ Personel / Xóa /' + id,
                    dữ liệu: dữ liệu,
                    bộ đệm: sai,
                    processData: sai,
                    ContentType: sai,
                    thành công: hàm (kết quả) {

                    }
                });

        }
    

        lớp tĩnh công cộng HtmlHelper
        {
            chuỗi tĩnh công khai GetAntiForgeryToken ()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match (System.Web.Helpers.AntiForgery.GetHtml (). ToString (), "(?: Value = \") (. *) (? : \ ")");
                if (value.Success)
                {
                    giá trị trả về.group [1]. Giá trị;
                }
                trở về "";
            }
        }

3

Tôi biết đây là một câu hỏi cũ. Nhưng dù sao tôi cũng sẽ thêm câu trả lời của mình, có thể giúp một người như tôi.

Nếu bạn không muốn xử lý kết quả từ hành động đăng của bộ điều khiển, như gọi LoggOffphương thức của Accountsbộ điều khiển, bạn có thể thực hiện như phiên bản sau của câu trả lời của @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

Trong bộ điều khiển tài khoản:

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

Trong chế độ xem:

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

Điều quan trọng để thiết lập là contentTypeđể 'application/x-www-form-urlencoded; charset=utf-8'hoặc chỉ bỏ qua contentTypetừ đối tượng ...


không thực sự thiết thực, có nghĩa là bạn phải mã hóa mọi hình thức và nếu các biểu mẫu có nhiều yếu tố thì đó có thể là một nỗi đau :(
djack109

0

Tôi đã thử rất nhiều cách giải quyết và không ai trong số họ làm việc cho tôi. Ngoại lệ là "Trường biểu mẫu chống giả mạo bắt buộc" __RequestVerifyingToken ".

Điều giúp tôi ra ngoài là chuyển từ .ajax sang .post:

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

0

Hãy sử dụng chức năng dưới đây:

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

Mã thông báo sẽ không hoạt động nếu được cung cấp bởi một bộ điều khiển khác. Ví dụ: nó sẽ không hoạt động nếu chế độ xem được trả về bởi Accountsbộ điều khiển, nhưng bạn POSTlại với Clientsbộ điều khiển.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.