Как передать токен 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, и не хочет путать вопросы.