提问人:OJ Raqueño 提问时间:1/23/2013 最后编辑:CommunityOJ Raqueño 更新时间:3/31/2023 访问量:231521
在 ajax post ASP.NET MVC 中包含防伪令牌
include antiforgerytoken in ajax post ASP.NET MVC
问:
我在使用 ajax 的 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 获取令牌的值,然后尝试 ajax 发布它。
下面是包含令牌的窗体,位于顶部母版页:
<form id="__AjaxAntiForgeryForm" action="#" method="post">
@Html.AntiForgeryToken()
</form>
答:
您错误地指定了 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>
评论
contentType
application/json
__RequestVerificationToken
application/x-www-form-urlencoded
$(this).data('url'),
我做的另一种(不那么 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
});
});
});
它似乎有效!
评论
@Html.AntiForgeryTokenForAjaxPost
'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
XElement.Parse(antiForgeryInputTag).Attribute("value").Value
var antiForgeryInputTag = helper.AntiForgeryToken().ToString(); return XElement.Parse(antiForgeryInputTag).Attribute("value").Value
我知道这是一个老问题。但无论如何我都会添加我的答案,可能会帮助像我这样的人。
如果你不想处理控制器的 post 操作的结果,比如调用控制器的方法,你可以按照以下版本的 @DarinDimitrov 的答案来操作:LoggOff
Accounts
@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>
就是这么简单!当您在 HTML 代码中使用时,这意味着服务器已对此页面进行了签名,并且从此特定页面发送到服务器的每个请求都有一个标志,该标志可以防止黑客发送虚假请求。因此,要使此页面由服务器进行身份验证,您应该执行两个步骤:@Html.AntiForgeryToken()
1.发送一个名为 and 的参数,以获取其值,使用以下代码:__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]
评论
$(htmlWithInputString).val()
<div class="js-html-anti-forgery-token" data-anti-forgery-token-html-input="@(Html.AntiForgeryToken().ToString())">
$($(".js-html-anti-forgery-token").data("antiForgeryTokenHtmlInput")).val()
我尝试了很多工作轮,但没有一个对我有用。例外情况是“所需的防伪表单字段”__RequestVerificationToken”。
对我有帮助的是将 .ajax 表单切换到 .post:
$.post(
url,
$(formId).serialize(),
function (data) {
$(formId).html(data);
});
在 Asp.Net MVC 中,使用 Razor 时,会创建一个带有名称的隐藏输入字段来存储令牌。如果要编写 AJAX 实现,则必须自己获取此令牌并将其作为参数传递给服务器,以便对其进行验证。@Html.AntiForgeryToken()
__RequestVerificationToken
第 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
评论
function DeletePersonel(id) { var data = new FormData(); data.append("__RequestVerificationToken", "@HtmlHelper.GetAntiForgeryToken()"); $.ajax({ type: 'POST', url: '/Personel/Delete/' + id, data: data, cache: false, processData: false, contentType: false, success: function (result) { } }); } public static class HtmlHelper { public static string GetAntiForgeryToken() { System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), "(?:value=\")(.*)(?:\")"); if (value.Success) { return value.Groups[1].Value; } return ""; } }
随意使用以下功能:
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
}
});
}
在 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();
}
或者使用 post 方法:
function DoSomething() {
var form = $('#my_form');
$.post("/something/todo/create", form.serialize());
}
评论
在 Account controller:
// 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
评论
如果令牌由其他控制器提供,则令牌将不起作用。例如,如果视图是由控制器返回的,而是您返回给控制器的,则该视图将不起作用。Accounts
POST
Clients
创建一个负责添加令牌的方法
var addAntiForgeryToken = function (data) {
data.__RequestVerificationToken = $("[name='__RequestVerificationToken']").val();
return data;
};
现在使用此方法,同时将数据/参数传递给 Action,如下所示
var Query = $("#Query").val();
$.ajax({
url: '@Url.Action("GetData", "DataCheck")',
type: "POST",
data: addAntiForgeryToken({ Query: Query }),
dataType: 'JSON',
success: function (data) {
if (data.message == "Success") {
$('#itemtable').html(data.List);
return false;
}
},
error: function (xhr) {
$.notify({
message: 'Error',
status: 'danger',
pos: 'bottom-right'
});
}
});
在这里,我的动作有一个字符串类型的参数
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult GetData( string Query)
{
@using (Ajax.BeginForm("SendInvitation", "Profile",
new AjaxOptions { HttpMethod = "POST", OnSuccess = "SendInvitationFn" },
new { @class = "form-horizontal", id = "invitation-form" }))
{
@Html.AntiForgeryToken()
<span class="red" id="invitation-result">@Html.ValidationSummary()</span>
<div class="modal-body">
<div class="row-fluid marg-b-15">
<label class="block">
</label>
<input type="text" id="EmailTo" name="EmailTo" placeholder="[email protected]" value="" />
</div>
</div>
<div class="modal-footer right">
<div class="row-fluid">
<button type="submit" class="btn btn-changepass-new">send</button>
</div>
</div>
}
对我来说,解决方案是将令牌作为标头发送,而不是作为 ajax 调用中的数据发送:
$.ajax({
type: "POST",
url: destinationUrl,
data: someData,
headers:{
"RequestVerificationToken": token
},
dataType: "json",
success: function (response) {
successCallback(response);
},
error: function (xhr, status, error) {
// handle failure
}
});
我一直在为此苦苦挣扎,并提出了一个既声明又不需要大量客户端或服务器更改的解决方案。 这适用于 ASPNET MVC .NET 4.8,但也可以在 .NET Core 中轻松完成。
简述:
- 使用 ASPNET MVC AntiForgery.Validate() 和 AntiForgery.GetTokens()。
- 将令牌作为隐藏字段保留在页面中。
- 从页面获取令牌,并使用 jQuery.ajaxSend() 透明地将其“推送”到所有 JSON 调用中。
- 使用 ValidateAntiForgeryJSONToken(自定义 FilterAttribute)处理收到的令牌。
好处:
- 只需使用 [ValidateAntiForgeryJSONToken] 修饰控制器操作即可。
- 添加一次令牌。
- 将令牌沿 JSON 集中推送。
代码如下:
ValidateAntiForgeryJSONToken attibute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ValidateAntiForgeryJSONToken : FilterAttribute, IAuthorizationFilter
{
public ValidateAntiForgeryJSONToken()
{
}
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException(nameof(filterContext));
var request = filterContext.RequestContext.HttpContext.Request;
if (request == null)
return;
var stream = request.InputStream;
var encoding = request.ContentEncoding;
var reader = new StreamReader(stream, encoding);
var json = reader.ReadToEnd();
request.InputStream.Position = 0;
var token = "";
try
{
JObject o = JObject.Parse(json);
var jToken = o.GetValue("ajaxAFT", StringComparison.InvariantCultureIgnoreCase);
token = jToken.ToString();
}
catch (Exception)
{
}
ValidateAntiForgeryToken(token);
}
public static void ValidateAntiForgeryToken(string token)
{
string cookieToken = "";
string formToken = "";
if (!String.IsNullOrWhiteSpace(token))
{
string[] tokens = token.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
try
{
AntiForgery.Validate(cookieToken, formToken);
}
catch (Exception)
{
throw new BaseException();
}
}
}
控制器
[HttpPost]
[ValidateAntiForgeryJSONToken]
public ActionResult TestAJAXCall(string id)
{
}
阿贾克斯拦截
'use strict';
$(document)
.ajaxSend(function (event, jqXHR, ajaxSettings) {
if (ajaxSettings !== undefined) {
try {
let payload = JSON.parse(ajaxSettings.data);
let token = payload.ajaxAFT;
if (token === undefined) {
token = $("#_ajaxAFT").val();
payload.ajaxAFT = token;
ajaxSettings.data = JSON.stringify(payload);
}
} catch (e) {
}
}
});
Razor 令牌注入
@Html.AjaxAntiForgeryToken()
Razor 令牌帮助程序
public static HtmlString AjaxAntiForgeryToken(this HtmlHelper helper)
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
var myToken = cookieToken + ":" + formToken;
var model = new FormHiddenInputFormModel()
{
Id = "_ajaxAFT",
Value = myToken
};
return helper.Partial("Forms/_FormHiddenInputText", model);
}
评论