Я чувствую себя здесь как продвинутый некромант, но через 4 года в MVC5 это все еще остается проблемой.
Для правильной обработки ajax-запросов токен защиты от подделки должен передаваться на сервер при ajax-вызовах. Интеграция его в ваши почтовые данные и модели является грязной и ненужной. Добавление токена в качестве пользовательского заголовка является чистым и пригодным для повторного использования - и вы можете настроить его так, чтобы вам не приходилось делать это каждый раз.
Существует исключение - ненавязчивый ajax не требует специальной обработки для вызовов ajax. Маркер передается как обычно в обычном скрытом поле ввода. Точно так же, как обычный POST.
_Layout.cshtml
В _layout.cshtml у меня есть этот блок JavaScript. Он не записывает токен в DOM, а использует jQuery для извлечения его из скрытого входного литерала, который генерирует помощник MVC. Волшебная строка, которая является именем заголовка, определяется как константа в классе атрибутов.
<script type="text/javascript">
$(document).ready(function () {
var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
//http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative
$.ajaxSetup({
beforeSend: function (xhr) {
if (!isAbsoluteURI.test(this.url)) {
//only add header to relative URLs
xhr.setRequestHeader(
'@.ValidateAntiForgeryTokenOnAllPosts.HTTP_HEADER_NAME',
$('@Html.AntiForgeryToken()').val()
);
}
}
});
});
</script>
Обратите внимание на использование одинарных кавычек в функции beforeSend - в отображаемом элементе ввода используются двойные кавычки, которые разбивают литерал JavaScript.
Клиентский JavaScript
Когда это выполняется, вызывается вышеуказанная функция beforeSend, и AntiForgeryToken автоматически добавляется в заголовки запроса.
$.ajax({
type: "POST",
url: "CSRFProtectedMethod",
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
//victory
}
});
Серверная библиотека
Для обработки нестандартного токена требуется пользовательский атрибут. Это основано на решении @ viggity, но обрабатывает ненавязчивый Ajax правильно. Этот код можно спрятать в вашей общей библиотеке
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
public const string HTTP_HEADER_NAME = "x-RequestVerificationToken";
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
// Only validate POSTs
if (request.HttpMethod == WebRequestMethods.Http.Post)
{
var headerTokenValue = request.Headers[HTTP_HEADER_NAME];
// Ajax POSTs using jquery have a header set that defines the token.
// However using unobtrusive ajax the token is still submitted normally in the form.
// if the header is present then use it, else fall back to processing the form like normal
if (headerTokenValue != null)
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null
? antiForgeryCookie.Value
: null;
AntiForgery.Validate(cookieValue, headerTokenValue);
}
else
{
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
}
}
}
}
Сервер / Контроллер
Теперь вы просто применяете атрибут к своему действию. Более того, вы можете применить атрибут к контроллеру, и все запросы будут проверены.
[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
return Json(true, JsonRequestBehavior.DenyGet);
}