$ .ajax를 사용하여 JSON 데이터를 게시 할 때 AntiForgeryToken을 어떻게 제공 할 수 있습니까?
이 게시물의 아래 코드를 사용하고 있습니다.
먼저 컨트롤러 작업에 대한 올바른 값으로 배열 변수를 채울 것입니다.
아래 코드를 사용하면 JavaScript 코드에 다음 줄을 추가하는 것만으로도 매우 간단해야한다고 생각합니다.
data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
은 <%= Html.AntiForgeryToken() %>
오른쪽 장소에서, 그리고 액션은있다[ValidateAntiForgeryToken]
하지만 내 컨트롤러 작업은 계속해서 "잘못된 위조 토큰"이라고 말합니다.
내가 여기서 뭘 잘못하고 있니?
암호
data["fiscalyear"] = fiscalyear;
data["subgeography"] = $(list).parent().find('input[name=subGeography]').val();
data["territories"] = new Array();
$(items).each(function() {
data["territories"].push($(this).find('input[name=territory]').val());
});
if (url != null) {
$.ajax(
{
dataType: 'JSON',
contentType: 'application/json; charset=utf-8',
url: url,
type: 'POST',
context: document.body,
data: JSON.stringify(data),
success: function() { refresh(); }
});
}
MVC 4 이후 ValidationHttpRequestWrapper 솔루션이 필요하지 않습니다 . 이 링크 에 따르면 .
- 헤더에 토큰을 넣으십시오.
- 필터를 만듭니다.
- 메소드에 속성을 입력하십시오.
내 해결책은 다음과 같습니다.
var token = $('input[name="__RequestVerificationToken"]').val();
var headers = {};
headers['__RequestVerificationToken'] = token;
$.ajax({
type: 'POST',
url: '/MyTestMethod',
contentType: 'application/json; charset=utf-8',
headers: headers,
data: JSON.stringify({
Test: 'test'
}),
dataType: "json",
success: function () {},
error: function (xhr) {}
});
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);
}
}
[HttpPost]
[AllowAnonymous]
[ValidateJsonAntiForgeryToken]
public async Task<JsonResult> MyTestMethod(string Test)
{
return Json(true);
}
잘못된 것은이 요청을 처리해야하고로 표시된 컨트롤러 작업이 요청과 함께 POST 될 [ValidateAntiForgeryToken]
매개 변수를 예상 __RequestVerificationToken
한다는 것입니다.
JSON.stringify(data)
양식을 JSON 표현으로 변환하는 데 사용 하는 POSTed 매개 변수가 없으므로 예외가 발생합니다.
따라서 여기에서 두 가지 가능한 솔루션을 볼 수 있습니다.
번호 1 : 요청 매개 변수를 보내는 x-www-form-urlencoded
대신 사용 JSON
:
data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
data["fiscalyear"] = fiscalyear;
// ... other data if necessary
$.ajax({
url: url,
type: 'POST',
context: document.body,
data: data,
success: function() { refresh(); }
});
2 번 : 요청을 두 개의 매개 변수로 분리합니다.
data["fiscalyear"] = fiscalyear;
// ... other data if necessary
var token = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: url,
type: 'POST',
context: document.body,
data: { __RequestVerificationToken: token, jsonRequest: JSON.stringify(data) },
success: function() { refresh(); }
});
따라서 모든 경우에 __RequestVerificationToken
값 을 게시해야 합니다.
현재 프로젝트에서이 실제 문제를 구현하고있었습니다. 인증 된 사용자가 필요한 모든 Ajax POST에 대해 수행했습니다.
먼저 jQuery Ajax 호출을 연결하여 너무 자주 반복하지 않기로 결정했습니다. 이 JavaScript 스 니펫은 모든 ajax (post) 호출이 요청에 내 요청 유효성 검사 토큰을 추가하도록합니다. 참고 : __RequestVerificationToken이라는 이름은 .NET 프레임 워크에서 사용되므로 아래와 같이 표준 Anti-CSRF 기능을 사용할 수 있습니다.
$(document).ready(function () {
securityToken = $('[name=__RequestVerificationToken]').val();
$('body').bind('ajaxSend', function (elm, xhr, s) {
if (s.type == 'POST' && typeof securityToken != 'undefined') {
if (s.data.length > 0) {
s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
else {
s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
}
});
});
위의 JavaScript 코드에서 토큰을 사용할 수 있어야하는 뷰에서 일반적인 HTML-Helper를 사용하면됩니다. 기본적으로 원하는 곳에이 코드를 추가 할 수 있습니다. if (Request.IsAuthenticated) 문 안에 배치했습니다.
@Html.AntiForgeryToken() // You can provide a string as salt when needed which needs to match the one on the controller
컨트롤러에서 표준 ASP.NET MVC anti-CSRF 메커니즘을 사용하면됩니다. (실제로 소금을 사용했지만) 이렇게했습니다.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public JsonResult SomeMethod(string param)
{
// Do something
return Json(true);
}
Firebug 또는 유사한 도구를 사용하면 이제 POST 요청에 __RequestVerificationToken 매개 변수가 추가되는 방식을 쉽게 확인할 수 있습니다.
당신은 설정할 수 있습니다 $ 아약스 의 traditional
속성을하고 설정 true
, URL 인코딩 된 형태로 데이터를 JSON 보낼 수 있습니다. 설정해야합니다 type:'POST'
. 이 방법을 사용하면 배열을 보낼 수도 있으며 JSON.stringyfy 또는 서버 측에서 변경 사항을 사용할 필요가 없습니다 (예 : 헤더를 스니핑하는 사용자 지정 속성 생성).
ASP.NET MVC3 및 jquery 1.7 설정에서 이것을 시도했으며 작동 중입니다.
다음은 코드 조각입니다.
var data = { items: [1, 2, 3], someflag: true};
data.__RequestVerificationToken = $(':input[name="__RequestVerificationToken"]').val();
$.ajax({
url: 'Test/FakeAction'
type: 'POST',
data: data
dataType: 'json',
traditional: true,
success: function (data, status, jqxhr) {
// some code after succes
},
error: function () {
// alert the error
}
});
이것은 다음 서명이있는 MVC 작업과 일치합니다.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult FakeAction(int[] items, bool someflag)
{
}
내 JSON 오브젝트의 토큰을 잡고 나는 확인하기 위해 ValidateAntiForgeryToken 클래스를 수정 결국 의 InputStream 의 요청 게시물은 JSON이 때 개체를. 나는 그것에 대한 블로그 게시물을 작성 했습니다. 도움이 되길 바랍니다.
게시 된 JSON을받을 때 AntiForgeryToken의 유효성을 검사 할 필요가 없습니다.
그 이유는 CSRF를 방지하기 위해 AntiForgeryToken이 만들어 졌기 때문입니다. AJAX 데이터를 다른 호스트에 게시 할 수없고 HTML 양식이 JSON을 요청 본문으로 제출할 수 없기 때문에 게시 된 JSON으로부터 앱을 보호 할 필요가 없습니다.
contentType : 'application / json; 유형의 콘텐츠를 확인할 수 없습니다. charset = utf-8 ' 은 요청 의 Form 속성이 아니라 InputStream 속성에 날짜가 업로드 되고이 Request.Form [ "__ RequestVerificationToken"]을 가질 수 없기 때문입니다.
이것은 항상 비어 있으며 유효성 검사가 실패합니다.
RequestHeader를 사용하여 전 세계적으로 해결했습니다.
$.ajaxPrefilter(function (options, originalOptions, jqXhr) {
if (options.type.toUpperCase() === "POST") {
// We need to add the verificationToken to all POSTs
if (requestVerificationTokenVariable.length > 0)
jqXhr.setRequestHeader("__RequestVerificationToken", requestVerificationTokenVariable);
}
});
여기서 requestVerificationTokenVariable은 토큰 값을 포함하는 변수 문자열입니다. 그런 다음 모든 ajax 호출이 토큰을 서버로 보내지 만 기본 ValidateAntiForgeryTokenAttribute는 Request.Form 값을 가져옵니다. 기본 ValidateAntiForgeryTokenAttribute를 사용할 수있는 것보다 헤더에서 request.form으로 토큰을 복사하는이 globalFilter를 작성하고 추가했습니다.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new GlobalAntiForgeryTokenAttribute(false));
}
public class GlobalAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly bool autoValidateAllPost;
public GlobalAntiForgeryTokenAttribute(bool autoValidateAllPost)
{
this.autoValidateAllPost = autoValidateAllPost;
}
private const string RequestVerificationTokenKey = "__RequestVerificationToken";
public void OnAuthorization(AuthorizationContext filterContext)
{
var req = filterContext.HttpContext.Request;
if (req.HttpMethod.ToUpperInvariant() == "POST")
{
//gestione per ValidateAntiForgeryToken che gestisce solo il recupero da Request.Form (non disponibile per le chiamate ajax json)
if (req.Form[RequestVerificationTokenKey] == null && req.IsAjaxRequest())
{
var token = req.Headers[RequestVerificationTokenKey];
if (!string.IsNullOrEmpty(token))
{
req.Form.SetReadOnly(false);
req.Form[RequestVerificationTokenKey] = token;
req.Form.SetReadOnly(true);
}
}
if (autoValidateAllPost)
AntiForgery.Validate();
}
}
}
public static class NameValueCollectionExtensions
{
private static readonly PropertyInfo NameObjectCollectionBaseIsReadOnly = typeof(NameObjectCollectionBase).GetProperty("IsReadOnly", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance);
public static void SetReadOnly(this NameValueCollection source, bool readOnly)
{
NameObjectCollectionBaseIsReadOnly.SetValue(source, readOnly);
}
}
이것은 나를 위해 일합니다 :)
Dixin의 블로그 에서 정확한 작업에 대한 훌륭한 게시물을 확인하십시오 .
또한 $ .ajax 대신 $ .post를 사용하지 않는 이유는 무엇입니까?
해당 페이지의 jQuery 플러그인과 함께 다음과 같이 간단한 작업을 수행 할 수 있습니다.
data = $.appendAntiForgeryToken(data,null);
$.post(url, data, function() { refresh(); }, "json");
JSON을 게시 할 때 위조 방지 토큰의 유효성을 검사하기 위해 약간 그늘이되어야했지만 작동했습니다.
//If it's not a GET, and the data they're sending is a string (since we already had a separate solution in place for form-encoded data), then add the verification token to the URL, if it's not already there.
$.ajaxSetup({
beforeSend: function (xhr, options) {
if (options.type && options.type.toLowerCase() !== 'get' && typeof (options.data) === 'string' && options.url.indexOf("?__RequestVerificationToken=") < 0 && options.url.indexOf("&__RequestVerificationToken=") < 0) {
if (options.url.indexOf('?') < 0) {
options.url += '?';
}
else {
options.url += '&';
}
options.url += "__RequestVerificationToken=" + encodeURIComponent($('input[name=__RequestVerificationToken]').val());
}
}
});
그러나 이미 몇 사람이 언급했듯이 유효성 검사는 쿼리 문자열이 아닌 JSON이 아닌 양식 만 확인합니다. 따라서 우리는 속성의 동작을 무시합니다. 모든 유효성 검사를 다시 구현하는 것은 끔찍한 일이었고 (아마 안전하지 않을 수도 있음), 토큰이 QueryString에 전달 된 경우 Form 속성에 기본 제공 유효성 검사가있는 THINK를 갖도록 Form 속성을 덮어 썼습니다.
양식이 읽기 전용이지만 실행 가능하기 때문에 약간 까다 롭습니다.
if (IsAuth(HttpContext.Current) && !IsGet(HttpContext.Current))
{
//if the token is in the params but not the form, we sneak in our own HttpContext/HttpRequest
if (HttpContext.Current.Request.Params != null && HttpContext.Current.Request.Form != null
&& HttpContext.Current.Request.Params["__RequestVerificationToken"] != null && HttpContext.Current.Request.Form["__RequestVerificationToken"] == null)
{
AntiForgery.Validate(new ValidationHttpContextWrapper(HttpContext.Current), null);
}
else
{
AntiForgery.Validate(new HttpContextWrapper(HttpContext.Current), null);
}
}
//don't validate un-authenticated requests; anyone could do it, anyway
private static bool IsAuth(HttpContext context)
{
return context.User != null && context.User.Identity != null && !string.IsNullOrEmpty(context.User.Identity.Name);
}
//only validate posts because that's what CSRF is for
private static bool IsGet(HttpContext context)
{
return context.Request.HttpMethod.ToUpper() == "GET";
}
...
internal class ValidationHttpContextWrapper : HttpContextBase
{
private HttpContext _context;
private ValidationHttpRequestWrapper _request;
public ValidationHttpContextWrapper(HttpContext context)
: base()
{
_context = context;
_request = new ValidationHttpRequestWrapper(context.Request);
}
public override HttpRequestBase Request { get { return _request; } }
public override IPrincipal User
{
get { return _context.User; }
set { _context.User = value; }
}
}
internal class ValidationHttpRequestWrapper : HttpRequestBase
{
private HttpRequest _request;
private System.Collections.Specialized.NameValueCollection _form;
public ValidationHttpRequestWrapper(HttpRequest request)
: base()
{
_request = request;
_form = new System.Collections.Specialized.NameValueCollection(request.Form);
_form.Add("__RequestVerificationToken", request.Params["__RequestVerificationToken"]);
}
public override System.Collections.Specialized.NameValueCollection Form { get { return _form; } }
public override string ApplicationPath { get { return _request.ApplicationPath; } }
public override HttpCookieCollection Cookies { get { return _request.Cookies; } }
}
우리 솔루션에는 다른 점이 있습니다 (특히 HttpModule을 사용하므로 모든 POST에 속성을 추가 할 필요가 없음). 필요한 경우 추가 할 수 있습니다.
AJAX based model posting with AntiForgerytoken can be made bit easier with Newtonsoft.JSON library
Below approach worked for me:
Keep your AJAX post like this:
$.ajax(
{
dataType: 'JSON',
url: url,
type: 'POST',
context: document.body,
data: {
'__RequestVerificationToken' : token,
'model_json': JSON.stringify(data)
}; ,
success: function() { refresh(); }
});
Then in your MVC action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(FormCollection data)
{
var model= JsonConvert.DeserializeObject<Order>(data["model_json"]);
return Json(1);
}
Hope this helps :)
Unfortunately for me, the other answers rely on some request formatting handled by jquery, and none of them worked when setting the payload directly. (To be fair, putting it in the header would have worked, but I did not want to go that route.)
To accomplish this in the beforeSend
function, the following works. $.params()
transforms the object into the standard form / url-encoded format.
I had tried all sorts of variations of stringifying json with the token and none of them worked.
$.ajax({
...other params...,
beforeSend: function(jqXHR, settings){
var token = ''; //get token
data = {
'__RequestVerificationToken' : token,
'otherData': 'value'
};
settings.data = $.param(data);
}
});
```
You should place AntiForgeryToken in a form tag:
@using (Html.BeginForm(actionName:"", controllerName:"",routeValues:null, method: FormMethod.Get, htmlAttributes: new { @class="form-validator" }))
{
@Html.AntiForgeryToken();
}
then in javascript modify the following code to be
var DataToSend = [];
DataToSend.push(JSON.stringify(data),$('form.form-validator').serialize());
$.ajax(
{
dataType: 'JSON',
contentType: 'application/json; charset=utf-8',
url: url,
type: 'POST',
context: document.body,
data: DataToSend,
success: function() { refresh(); }
});
then you should be able to validate the request using ActionResult annotations
[ValidateAntiForgeryToken]
[HttpPost]
i hope this helps.
'programing tip' 카테고리의 다른 글
행렬을 하나의 열로 하위 집합하고, 행렬 데이터 유형을 유지하고, 행 / 열 이름을 유지하는 방법은 무엇입니까? (0) | 2020.11.02 |
---|---|
TFS의 작업 영역에서 현재 변경 집합 ID 가져 오기 (0) | 2020.11.02 |
Visual Studio에서 {를 새 줄에 넣지 않도록하는 방법? (0) | 2020.11.01 |
루비 1.9.3의 루비 디버그? (0) | 2020.11.01 |
System.Web.Mvc.HtmlHelper가 표시되지 않는 Razor보기 (0) | 2020.11.01 |