Проблема проверки токена CSRF с Symfony и AJAX с пользовательским методом DELETE

Я столкнулся с проблемой при попытке проверить токен CSRF в моем приложении Symfony при использовании AJAX для удаления выбранных элементов. Вот обзор моей установки:

При попытке удалить выбранные элементы с помощью запроса AJAX я получаю сообщение об ошибке, указывающее, что токен CSRF недействителен, хотя я убедился, что токены совпадают между клиентом и сервером.

Код HTML-форма:

<tbody>
    {% for annonce in allAnnonces %}
    
        <tr class = "text-dark" id = "annonce_ids{{ annonce.id }}">
            <td>
    
                <input type = "checkbox" name = "ids" class = "checkbox_ids" id = "" value = "{{ annonce.id }}">
                <input type = "hidden" name = "_method" value = "DELETE">
                <input type = "hidden" name = "_tokenannonce" value = "{{ csrf_token('annonce' ~ annonce.id) }}">
    
            </td>
            <td>
                {{ annonce.marques }} {{ annonce.modeles }} {{ annonce.year }}
            </td>
            <td>
                {{ annonce.price | number_format(0, ',', ' ') }} {{ annonce.device }}
            </td>
            ........
    
            <td>
                <a title = "Éditer l'annonce" class = "jsvm_cm-actn-btn p-1" href = "{{ path('app_compte_annonceur_edit',{'id': annonce.id })}}">
                    <img src = "{{ asset('/images/annonces/edit-icon.png')}}" alt = "Modifier l'annonce" title = "Éditer l'annonce">
                </a>
    
                <form method = "POST" action = "{{ path('app_compte_annonceur_delete', {id: annonce.id}) }}" style = "display: inline-block" onsubmit = "return confirm('Êtes vous vraiment sûr ?')">
                    <input type = "hidden" name = "_method" value = "DELETE">
                    <input type = "hidden" name = "_token" value = "{{ csrf_token('delete' ~ annonce.id) }}">
                    <button class = "btn" title = "supprimer" type = "submit">
                        <img src = "{{ asset('/images/annonces/delete.png')}}" alt = "Supprimer l'annonce" title = "Supprimer l'annonce">
                    </button>
                </form>
            </td>
        </tr>
    {% endfor %}
</tbody>
<button class = "jsvm_cm-pro-dashb-myveh-btn jsvm_cm-dlt-btn jsvm_multioperation-frontend border btn btn-danger p-2 button red"

        data-for = "removemulti"
        data-formid = "jsvehiclemanager-list-form"
        id = "delete-selected-btn"
        type = "submit"
        data-delete-url = "{{ path('app_delete_selected') }}">
    <img src = "{{ asset('/images/annonces/delete.png')}}"

    <font style = "vertical-align: inherit;">
        <font style = "vertical-align: inherit;">
            delete selected row</font>
    </font>
    </span>
</button>

Контроллер Symfony:

#[Route('/dashbord/Annonces/deleteselected', name: 'app_delete_selected', methods: 'DELETE')]
    public function deleteSelected(Request $request): JsonResponse
    {
        $message = [];
        
        $token = $request->get('_tokenannonce');
        $ids= $request->get('ids');
        // dump($data);
        dump($ids);
        dump($token);
        if (!empty($ids)) {
            
            $annonces = $this->annonceRepository->findBy(['id' => $ids]);
            dump($annonces);
            foreach ($annonces as $annonce) {
                
                dump($annonce->getId());
                // dump($this->->getToken('csrf_token')->getValue());
                dump($this->isCsrfTokenValid('annonce'.$annonce->getId(), $token));
                if ($this->isCsrfTokenValid('annonce'.$annonce->getId(), $token)) {
                    $this->em->remove($annonce);
                    $this->em->flush();
                    $this->addFlash('success', 'success');
                    $message = ['success' => true, 'message' => 'Success'];
                } else {
                    $this->addFlash('error', 'Invalid token');
                    $message = ['success' =>false, 'message' => 'Invalid token'];
                }
            }
        dump($message);
        return new JsonResponse(['message' => $message]);
    }

JavaScript AJAX-скрипт:

// Handle multiple selection and row removal
$('#delete-selected-btn').click(function () {
    //var selectedRows = oTable.rows('.selected').data();
    var selectedRows = oTable.rows(".selected").nodes();
    var csrfToken = $('input[name = "_tokenannonce"]').val();
    console.info(csrfToken);
    if (selectedRows.length > 0) {
        // Extract IDs from selected rows
       // var selectedIds = selectedRows.toArray().map(row => row[0]); // Assuming the ID is in the first column
        var selectedIds = []; 
         $(selectedRows).each(function () {
           var id = $(this).find("td:first-child input").val(); 
           selectedIds.push(id);
         });
        console.info(selectedIds);
         
        // Send AJAX request to delete selected rows
        console.info($(this).data('delete-url'));
        $.ajax({
          url: $(this).data("delete-url"),
          type: "DELETE",
          dataType: "json",
          processData: true,
          data: {
            ids: selectedIds,
           _tokenannonce: csrfToken
          },
          headers: {
            "_tokenannonce": csrfToken, // Include the CSRF token in the request headers
          },
          traditional: true,
          success: function (response) {
            console.info(response);
            if (response.success) {
              alert("success");
              oTable.rows(".selected").fadeOut().remove().draw(false);
            } else {
              alert("Erreur");
            }
          },
          error: function (xhr, status, error) {
            console.info(error);
            console.info(status);
            console.info(xhr);
            // alert('Erreur lors de la requête Ajax');
          },
          beforeSend: function (data) {
            $(selectedRows).css({
              "background-color": "#ccc",
              color: "#fff", // Change the text color for better visibility
            });
            console.info("Data sent:", data); 
          },
        });
    } else {
        alert('Select a row!');
    }
});

Что я пробовал Я несколько раз проверил, что имя поля CSRF в форме соответствует тому, которое я использую в своем запросе AJAX. Я использовал dump() в Symfony, чтобы убедиться, что токен CSRF правильно получен в контроллере.

Вопрос Почему, несмотря на все эти проверки, я продолжаю получать сообщение об ошибке недействительного токена CSRF? Есть ли что-то, что мне не хватает или на что следует обратить внимание?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
0
173
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец, я понял, выполнив:

Контроллер:

$tokens = $request->get('_tokenannonce');
foreach ($annonces as $key => $annonce) {
    $token = $tokens[$key];
    .......
}

аякс-код:

// Toggle the selected class on row click
oTable.on('click', 'tbody tr', function () {
    $(this).toggleClass('selected');
});

// Handle multiple selection and row removal
$("#delete-selected-btn").click(function () {

  var selectedRows = oTable.rows(".selected").nodes();
  var selectedIds = $(selectedRows)
    .map(function () {
      return $(this).find("td:first-child input").val();
    })
    .get();
  var tokens = $(selectedRows)
    .map(function () {
      return $(this).find("td:first-child input[name='_tokenannonce']").val();
    })
    .get();

  if (selectedIds.length > 0) {
    $.ajax({
      url: $(this).data("delete-url"),
      type: "DELETE",
      dataType: "json",
      data: {
        ids: selectedIds,
        _tokenannonce: tokens,
      },
      success: function (response) {
        if (response.success) {
          //alert(response.message);
          oTable.rows(".selected").remove().draw(false);
        } else {
          alert(response.message);
        }
      },
      fail: function (xhr, status, error) {
        alert("Error AJAX : " + status + " - " + error);
      },
      beforeSend: function () {
        $(selectedRows).css({
          "background-color": "#ccc",
          color: "#fff",
        });
      },
    });
  
});

Другие вопросы по теме