Ajax post ASP.NET MVC에 antiforgerytoken 포함


168

AntiForgeryToken에 아약스 문제가 있습니다. 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.속성을 다시 넣으면 메시지가 계속 나타납니다.

어떤 아이디어?

편집하다

위조 방지 토큰이 양식 내부에서 생성되고 있지만 제출 조치를 사용하여 제출하지 않습니다. 대신 jquery를 사용하여 토큰의 가치를 얻은 다음 아약스를 게시하려고합니다.

토큰을 포함하고 맨 위 마스터 페이지에있는 양식은 다음과 같습니다.

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

답변:


289

를 잘못 지정 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>

빠른 답변 감사합니다. 미안하지만 질문에 언급하지 않았습니다. 현재 제출 작업을 사용하고 있지 않습니다. (토큰은 양식이지만 제출 버튼을 사용하여 제출하지 않습니다). 컨텐츠 유형을 다른 것으로 변경할 수 있습니까?
OJ Raqueño

13
제출 조치를 사용하지 않는다고해서 내 대답이 크게 바뀌지는 않습니다. 다른 이벤트 (버튼 클릭, 앵커 클릭 등)를 구독하기 만하면 숨겨진 필드 값을 읽을 수 있습니다. AJAX 요청을 보내는 한 내 대답에 제공된 예제를 사용할 수 있습니다. 당신은 사용하지 말아야 contentTypeapplication/json서버가 기대되기 때문에 __RequestVerificationToken매개 변수를 사용하여 POST 요청 페이로드의 일부가 될 application/x-www-form-urlencoded.
Darin Dimitrov

이 코드 $(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한 손으로 토큰 이름을 얻고 다른 손으로 토큰 이름을 얻기 위해 두 개로 나눕니다 . 그렇지 않으면 구문 강조 표시가 모두 엉망입니다. 다음과 같이 끝납니다 (반환 된 결과에서 작은 따옴표도 제거하여 @VCL과 같은 MVC 도우미처럼 동작 함) :'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein

4
좋은데 이것으로 당신은 아약스 호출 n cshtm 파일을 가지고 있습니다 .... 당신은 내 의견으로는 면도기를 많이 사용하여 js를 mox해서는 안됩니다.
bunny1985

더 간단한 접근 방식이 AntiForgery 정적 클래스를 사용하는 것이라고 생각하기 때문에이 질문을 downvoted했습니다. 토큰 값을 직접 얻는 대신 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({
    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;
    }
}

그리고 자바 스크립트에서 사용하십시오 :

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-학생) 직렬화 () : 어떻게 내가 여기 학생 형태의 직렬화를 사용할 수 있습니다
LittleDragon

6

        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 값 = System.Text.RegularExpressions.Regex.Match (System.Web.Helpers.AntiForgery.GetHtml (). ToString (), "(? : value = \") (. *) (? : \ ")");
                if (value.Success)
                {
                    반환 값. 그룹 [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 "은 예외입니다.

나를 도운 것은 .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당신은 컨트롤러하지만 POST받는 Clients컨트롤러.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.