Как передать токен CSRF в почтовом запросе AJAX для формы?
Я использую Scala Play! 2.6, но это может и не быть проблемой. Я использую их маршрутизацию Javascript - и, похоже, он работает нормально, но у него есть проблемы. У меня есть форма, которая при визуализации производит это с помощью токена CSRF:
<form method="post" id="myForm" action="someURL">
<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
<input type="text" id="sometext">
<button type="submit"> Submit! </button>
</form>
И вот примерно, мой AJAX:
$(document).on('submit', '#myForm', function (event) {
event.preventDefault();
var data = {
textvalue: $('#sometext').val()
}
var route = jsRoutes.controllers.DashboardController.postNewProject()
$.ajax({
url: route.url,
type: route.type,
data : JSON.stringify(data),
contentType : 'application/json',
success: function (data) { ... },
error: function (data) { ... }
})
});
Но когда я отправляю это сообщение, я получаю ответ UNAUTHORIZED с моего сервера, и моя консоль в IntelliJ сообщает мне, что проверка CSRF не работает. Как бы я пропустил токен CSRF в запросе?
Ответы
Ответ 1
Хорошо, после нескольких часов борьбы с этим и попытки расшифровать документацию часто-недостающего контекста по теме, я понял.
Итак, из их документов:
Чтобы обеспечить простую защиту для запросов не из браузера, Play only проверяет запросы с куки в шапке. Если вы делаете запросы с AJAX, вы можете поместить токен CSRF на страницу HTML, а затем добавить его на запрос с использованием заголовка Csrf-Token
.
И тут нет ни кода, ни примера. Спасибо Play. Очень наглядно. В любом случае, вот как:
в вашем view.html.formTemplate
вы можете написать в IntelliJ:
@()
<form method="post" id="myForm" action="someURL">
@helper.CSRF.formField <!-- This auto-generates a token for you -->
<input type="text" id="sometext">
<button type="submit"> Submit! </button>
</form>
И это будет выглядеть следующим образом при доставке клиенту:
<form method="post" id="myForm" action="someURL">
<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
<input type="text" id="sometext">
<button type="submit"> Submit! </button>
</form>
Хорошо, почти там, теперь мы должны создать наш вызов AJAX. У меня есть все в отдельном файле main.js, но вы также можете поместить это в свой view.html.formTemplate
, если хотите.
$(document).on('submit', '#myForm', function (event) {
event.preventDefault();
var data = {
myTextToPass: $('#sometext').val()
}
// LOOK AT ME! BETWEEN HERE AND
var token = $('input[name="csrfToken"]').attr('value')
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('Csrf-Token', token);
}
});
// HERE
var route = jsRoutes.controllers.DashboardController.postNewProject()
$.ajax({
url: route.url,
type: route.type,
data : JSON.stringify(data),
contentType : 'application/json',
success: function (data) { ... },
error: function (data) { ... }
})
});
С этой строкой:
var token = $('input[name="csrfToken"]').attr('value')
Вы извлекаете токен CSRF, автоматически сгенерированный в поле формы, и захватываете его значение в переменной, которая будет использоваться в вашем Javascript.
Другой важный кусок из всего, что есть в AJAX:
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('Csrf-Token', token);
}
});
Используя $.ajaxSetup
, вы можете установить, что в заголовке. Вот что вы должны сделать из их документации:
добавьте его в запрос, используя заголовок Csrf-Token.
Удачи! Дайте мне знать, если это ясно.
Примечание: при использовании lusca используйте X-CSRF-Token
вместо Csrf-Token
.
Ответ 2
добавьте его в запрос с помощью заголовка Csrf-Token
.
Спасибо NateH06 за имя заголовка! Я пытался отправить токен csrf для кнопки "delete" с вызовом функции ajax, и я застрял на следующем:
@import helper._
....
<button id="deleteBookBtn" class="btn btn-danger"
data-csrf-name="@helper.CSRF.getToken.name"
data-csrf-value="@helper.CSRF.getToken.value"
data-delete-url="@routes.BooksController.destroy(book.id)"
data-redirect-url="@routes.HomeController.index()">Delete</button>
Мне не удалось добавить онлайн-js в событие onclick()
из-за установки CSP в игре 2.6.
Отказано в выполнении обработчика встроенного события, поскольку он нарушает следующую директиву политики безопасности содержимого: "default-src" self ".
И в файле JS:
function sendDeleteRequest(event) {
url = event.target.getAttribute("data-delete-url")
redirect = event.target.getAttribute("data-redirect-url")
csrfTokenName = event.target.getAttribute("data-csrf-name")
csrfTokenValue = event.target.getAttribute("data-csrf-value")
$.ajax({
url: url,
method: "DELETE",
beforeSend: function(request) {
//'Csrf-Token' is the expected header name, not $csrfTokenName
request.setRequestHeader(/*$csrfTokenName*/'Csrf-Token', csrfTokenValue);
},
success: function() {
window.location = redirect;
},
error: function() {
window.location.reload();
}
})
}
var deleteBookBtn = document.getElementById("deleteBookBtn");
if(deleteBookBtn) {
deleteBookBtn.addEventListener("click", sendDeleteRequest);
}
После установки имени заголовка как 'Csrf-Token'
вызов ajax отлично работает!
Ответ 3
Из JSP
<form method="post" id="myForm" action="someURL">
<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
</form>
Это самый простой способ, который сработал для меня после борьбы в течение 3 часов, просто получите токен из скрытого входного поля, как это, и при выполнении AJAX-запроса просто нужно передать этот токен в заголовок следующим образом: -
Из JQuery
var token = $('input[name="csrfToken"]').attr('value');
Из простого Javascript
var token = document.getElementsByName("csrfToken").value;
Финальный запрос AJAX
$.ajax({
url: route.url,
data : JSON.stringify(data),
method : 'POST',
headers: {
'X-CSRF-Token': token
},
success: function (data) { ... },
error: function (data) { ... }
});
Теперь вам не нужно отключать защиту crsf в веб-конфигурации, а также это не приведет к ошибке 405 (метод не разрешен) на консоли.
Надеюсь, это поможет людям.. !!
Ответ 4
Вы можете добавить заголовок Csrf-Token
с параметром headers
.
$.ajax({
url: '@routes.MyController.myPostAction()',
method: 'post',
headers: {
'Csrf-Token': '@play.filters.csrf.CSRF.getToken.map(_.value)'
},
data: {
name: '@name'
},
success: function (data, textStatus, jqXHR) {
location.reload();
},
error: function (jqXHR, textStatus, errorThrown) {
debugger;
}
});
Ответ 5
Как указано в документации Play Framework 2.6, вы можете установить заголовок Csrf-Token
с токеном, сгенерированным Play:
Если вы делаете запросы с помощью AJAX, вы можете поместить токен CSRF на HTML-странице, а затем добавить его в запрос, используя заголовок Csrf-Token
.
В Scala-Template вы можете получить значение токена, используя @helper.CSRF.getToken.value
Следуя документации jQuerys, вы можете установить ее один раз для всех запросов Ajax, настроив jQuery с помощью ajaxSetup
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('Csrf-Token', '@helper.CSRF.getToken.value');
}
});
или альтернативно установите заголовок для каждого запроса, настроив объект headers
следующим образом:
$.ajax({
url: route.url,
...
headers: {
'Csrf-Token': '@helper.CSRF.getToken.value'
}
});
Ответ 6
В случае, если он полезен всем, кто ищет в Google, пытаясь понять, почему они не могут получить правильный токен.... Я боролся с той же проблемой для комбинации Play backend/React - и, следовательно, неспособный (легко) использовать технику token-in-htmlpage, я в конце концов наткнулся на другое решение для установки текущего токена в cookie... просто добавьте:
play.filters.csrf {
cookie.name = "csrftoken"
}
to application.conf, и cookie csrftoken
будет установлен на токен. Затем я использовал https://www.npmjs.com/package/js-cookie, чтобы получить значение в моем коде JS и отправить его обратно в заголовок запроса - не считая код здесь, поскольку он реагирует не на jQuery, как для OP, и не хочет путать вопросы.