Intereting Posts
Изображения сайта фиксируются перед содержимым моего сайта Query Custom Post Тип сообщения по ключевому слову / url slug Не показывать таксономию, если пустая Как добавить роль WordPress, которая может видеть только запланированные и ожидающие сообщения? Создайте статический HTML-сайт из WordPress Проблемы с плагином, который извлекает данные из базы данных с помощью ajax Разбивка страницы WordPress не работает со страницей поиска Динамически созданная страница URL и содержимого the_post_thumbnail выполняет 2 запроса. Как оптимизировать Почему для функции обновления виджетов требуется $ old_instance? Абоненты получают только фрагмент моего сообщения Shortcode В верхней части страницы – PHP 5.5 vs 5.4 – ob_start (); Разница между вызовом wp_enqueue_scripts для загрузки скриптов и стилей в пользовательскую тему Включение пользовательских типов сообщений в виджет «Последние сообщения» Проблема с тегом <code>

Как получить уникальный nonce для каждого запроса Ajax?

Я видел пару обсуждений о том, как заставить WordPress восстанавливать уникальный nonce для последующих запросов Ajax, но для жизни я не могу заставить WordPress делать это – каждый раз, когда я прошу, что я думаю, должен быть новым nonce, я получаю тот же nonce обратно из WordPress. Я понимаю концепцию nonce_life WP и даже настраиваю ее на что-то еще, но это мне не помогло.

Я не генерирую nonce в объекте JS в заголовке через локализацию – я делаю это на своей странице отображения. Я могу заставить мою страницу обрабатывать запрос Ajax, но когда я запрашиваю новое nonce из WP в обратном вызове, я получаю тот же nonce back, и я не знаю, что я делаю неправильно … В конечном счете, я хочу расширьте это, чтобы на странице могло быть несколько элементов, каждый из которых имеет возможность добавлять / удалять – поэтому мне нужно решение, которое позволит несколько последующих запросов Ajax с одной страницы.

(И я должен сказать, что я включил все эти функции в плагин, поэтому передняя «страница дисплея» на самом деле является функцией, включенной в плагин …)

functions.php: локализовать, но я не создаю nonce здесь

wp_localize_script('myjs', 'ajaxVars', array('ajaxurl' => 'admin-ajax.php'))); 

Вызов JS:

 $("#myelement").click(function(e) { e.preventDefault(); post_id = $(this).data("data-post-id"); user_id = $(this).data("data-user-id"); nonce = $(this).data("data-nonce"); $.ajax({ type: "POST", dataType: "json", url: ajaxVars.ajaxurl, data: { action: "myfaves", post_id: post_id, user_id: user_id, nonce: nonce }, success: function(response) { if(response.type == "success") { nonce = response.newNonce; ... other stuff } } }); }); 

Получение PHP:

 function myFaves() { $ajaxNonce = 'myplugin_myaction_nonce_' . $postID; if (!wp_verify_nonce($_POST['nonce'], $ajaxNonce)) exit('Sorry!'); // Get various POST vars and do some other stuff... // Prep JSON response & generate new, unique nonce $newNonce = wp_create_nonce('myplugin_myaction_nonce_' . $postID . '_' . str_replace('.', '', gettimeofday(true))); $response['newNonce'] = $newNonce; // Also let the page process itself if there is no JS/Ajax capability } else { header("Location: " . $_SERVER["HTTP_REFERER"]; } die(); } 

Функция отображения Frontend PHP, среди которой:

 $nonce = wp_create_nonce('myplugin_myaction_nonce_' . $post->ID); $link = admin_url('admin-ajax.php?action=myfaves&post_id=' . $post->ID . '&user_id=' . $user_ID . '&nonce=' . $nonce); echo '<a id="myelement" data-post-id="' . $post->ID . '" data-user-id="' . $user_ID . '" data-nonce="' . $nonce . '" href="' . $link . '">My Link</a>'; 

На этом этапе я был бы очень благодарен за любые подсказки или указатели на то, что WP будет регенерировать уникальный nonce для каждого нового запроса Ajax …


ОБНОВЛЕНИЕ: Я решил свою проблему. Фрагменты кода выше действительны, однако я изменил создание $ newNonce в обратном вызове PHP, чтобы добавить строку микросекунд, чтобы убедиться, что она уникальна при последующих запросах Ajax.

Вот очень длинный ответ на мой собственный вопрос, который выходит за рамки простого решения вопроса о создании уникальных nonces для последующих запросов Ajax. Это функция «добавить в избранное», которая была сделана универсальной для целей ответа (моя функция позволяет пользователям добавлять идентификаторы сообщений вложений в список избранных, но это может относиться к множеству других функций, которые полагаются на Ajax). Я закодировал это как автономный плагин, и осталось несколько элементов – но это должно быть достаточно подробно, чтобы предоставить суть, если вы хотите воспроизвести эту функцию. Он будет работать на отдельной странице / странице, но он также будет работать в списках сообщений (например, вы можете добавлять / удалять элементы в избранное в строке через Ajax, и каждый пост будет иметь свой собственный уникальный nonce для каждого запроса Ajax). Имейте в виду, что есть, вероятно, более эффективный и / или более элегантный способ сделать это, и в настоящее время это работает только для Ajax. Я еще не потрудился обрабатывать данные, отличные от Ajax $ _POST.

scripts.php

 /** * Enqueue front-end jQuery */ function enqueueFavoritesJS() { // Only show Favorites Ajax JS if user is logged in if (is_user_logged_in()) { wp_enqueue_script('favorites-js', MYPLUGIN_BASE_URL . 'js/favorites.js', array('jquery')); wp_localize_script('favorites-js', 'ajaxVars', array('ajaxurl' => admin_url('admin-ajax.php'))); } } add_action('wp_enqueue_scripts', 'enqueueFavoritesJS'); 

favorites.js (Много отладочных вещей, которые можно удалить)

 $(document).ready(function() { // Toggle item in Favorites $(".faves-link").click(function(e) { // Prevent self eval of requests and use Ajax instead e.preventDefault(); var $this = $(this); console.log("Starting click event..."); // Fetch initial variables from the page post_id = $this.attr("data-post-id"); user_id = $this.attr("data-user-id"); the_toggle = $this.attr("data-toggle"); ajax_nonce = $this.attr("data-nonce"); console.log("data-post-id: " + post_id); console.log("data-user-id: " + user_id); console.log("data-toggle: " + the_toggle); console.log("data-nonce: " + ajax_nonce); console.log("Starting Ajax..."); $.ajax({ type: "POST", dataType: "json", url: ajaxVars.ajaxurl, data: { // Send JSON back to PHP for eval action : "myFavorites", post_id: post_id, user_id: user_id, _ajax_nonce: ajax_nonce, the_toggle: the_toggle }, beforeSend: function() { if (the_toggle == "y") { $this.text("Removing from Favorites..."); console.log("Removing..."); } else { $this.text("Adding to Favorites..."); console.log("Adding..."); } }, success: function(response) { // Process JSON sent from PHP if(response.type == "success") { console.log("Success!"); console.log("New nonce: " + response.newNonce); console.log("New toggle: " + response.theToggle); console.log("Message from PHP: " + response.message); $this.text(response.message); $this.attr("data-toggle", response.theToggle); // Set new nonce _ajax_nonce = response.newNonce; console.log("_ajax_nonce is now: " + _ajax_nonce); } else { console.log("Failed!"); console.log("New nonce: " + response.newNonce); console.log("Message from PHP: " + response.message); $this.parent().html("<p>" + response.message + "</p>"); _ajax_nonce = response.newNonce; console.log("_ajax_nonce is now: " + _ajax_nonce); } }, error: function(e, x, settings, exception) { // Generic debugging var errorMessage; var statusErrorMap = { '400' : "Server understood request but request content was invalid.", '401' : "Unauthorized access.", '403' : "Forbidden resource can't be accessed.", '500' : "Internal Server Error", '503' : "Service Unavailable" }; if (x.status) { errorMessage = statusErrorMap[x.status]; if (!errorMessage) { errorMessage = "Unknown Error."; } else if (exception == 'parsererror') { errorMessage = "Error. Parsing JSON request failed."; } else if (exception == 'timeout') { errorMessage = "Request timed out."; } else if (exception == 'abort') { errorMessage = "Request was aborted by server."; } else { errorMessage = "Unknown Error."; } $this.parent().html(errorMessage); console.log("Error message is: " + errorMessage); } else { console.log("ERROR!!"); console.log(e); } } }); // Close $.ajax }); // End click event }); 

Функции (внешний дисплей и действие Ajax)

Чтобы вывести ссылку «Добавить / Удалить Избранное», просто назовите ее на своей странице / странице через:

 if (function_exists('myFavoritesLink') { myFavoritesLink($user_ID, $post->ID); } 

Функция фронтального дисплея:

 function myFavoritesLink($user_ID, $postID) { global $user_ID; if (is_user_logged_in()) { // Set initial element toggle value & link text - udpated by callback $myUserMeta = get_user_meta($user_ID, 'myMetadata', true); if (is_array($myUserMeta['metadata']) && in_array($postID, $myUserMeta['metadata'])) { $toggle = "y"; $linkText = "Remove from Favorites"; } else { $toggle = "n"; $linkText = "Add to Favorites"; } // Create Ajax-only nonce for initial request only // New nonce returned in callback $ajaxNonce = wp_create_nonce('myplugin_myaction_' . $postID); echo '<p class="faves-action"><a class="faves-link"' . ' data-post-id="' . $postID . '" data-user-id="' . $user_ID . '" data-toggle="' . $toggle . '" data-nonce="' . $ajaxNonce . '" href="#">' . $linkText . '</a></p>' . "\n"; } else { // User not logged in echo '<p>Sign in to use the Favorites feature.</p>' . "\n"; } } 

Функция действия Ajax:

 /** * Toggle add/remove for Favorites */ function toggleFavorites() { if (is_user_logged_in()) { // Verify nonce $ajaxNonce = 'myplugin_myaction' . $_POST['post_id']; if (! wp_verify_nonce($_POST['_ajax_nonce'], $ajaxNonce)) { exit('Sorry!'); } // Process POST vars if (isset($_POST['post_id']) && is_numeric($_POST['post_id'])) { $postID = $_POST['post_id']; } else { return; } if (isset($_POST['user_id']) && is_numeric($_POST['user_id'])) { $userID = $_POST['user_id']; } else { return; } if (isset($_POST['the_toggle']) && ($_POST['the_toggle'] === "y" || $_POST['the_toggle'] === "n")) { $toggle = $_POST['the_toggle']; } else { return; } $myUserMeta = get_user_meta($userID, 'myMetadata', true); // Init myUserMeta array if it doesn't exist if ($myUserMeta['myMetadata'] === '' || ! is_array($myUserMeta['myMetadata'])) { $myUserMeta['myMetadata'] = array(); } // Toggle the item in the Favorites list if ($toggle === "y" && in_array($postID, $myUserMeta['myMetadata'])) { // Remove item from Favorites list $myUserMeta['myMetadata'] = array_flip($myUserMeta['myMetadata']); unset($myUserMeta['myMetadata'][$postID]); $myUserMeta['myMetadata'] = array_flip($myUserMeta['myMetadata']); $myUserMeta['myMetadata'] = array_values($myUserMeta['myMetadata']); $newToggle = "n"; $message = "Add to Favorites"; } else { // Add item to Favorites list $myUserMeta['myMetadata'][] = $postID; $newToggle = "y"; $message = "Remove from Favorites"; } // Prep for the response // Nonce for next request - unique with microtime string appended $newNonce = wp_create_nonce('myplugin_myaction_' . $postID . '_' . str_replace('.', '', gettimeofday(true))); $updateUserMeta = update_user_meta($userID, 'myMetadata', $myUserMeta); // Response to jQuery if($updateUserMeta === false) { $response['type'] = "error"; $response['theToggle'] = $toggle; $response['message'] = "Your Favorites could not be updated."; $response['newNonce'] = $newNonce; } else { $response['type'] = "success"; $response['theToggle'] = $newToggle; $response['message'] = $message; $response['newNonce'] = $newNonce; } // Process with Ajax, otherwise process with self if (! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { $response = json_encode($response); echo $response; } else { header("Location: " . $_SERVER["HTTP_REFERER"]); } exit(); } // End is_user_logged_in() } add_action('wp_ajax_myFavorites', 'toggleFavorites'); с /** * Toggle add/remove for Favorites */ function toggleFavorites() { if (is_user_logged_in()) { // Verify nonce $ajaxNonce = 'myplugin_myaction' . $_POST['post_id']; if (! wp_verify_nonce($_POST['_ajax_nonce'], $ajaxNonce)) { exit('Sorry!'); } // Process POST vars if (isset($_POST['post_id']) && is_numeric($_POST['post_id'])) { $postID = $_POST['post_id']; } else { return; } if (isset($_POST['user_id']) && is_numeric($_POST['user_id'])) { $userID = $_POST['user_id']; } else { return; } if (isset($_POST['the_toggle']) && ($_POST['the_toggle'] === "y" || $_POST['the_toggle'] === "n")) { $toggle = $_POST['the_toggle']; } else { return; } $myUserMeta = get_user_meta($userID, 'myMetadata', true); // Init myUserMeta array if it doesn't exist if ($myUserMeta['myMetadata'] === '' || ! is_array($myUserMeta['myMetadata'])) { $myUserMeta['myMetadata'] = array(); } // Toggle the item in the Favorites list if ($toggle === "y" && in_array($postID, $myUserMeta['myMetadata'])) { // Remove item from Favorites list $myUserMeta['myMetadata'] = array_flip($myUserMeta['myMetadata']); unset($myUserMeta['myMetadata'][$postID]); $myUserMeta['myMetadata'] = array_flip($myUserMeta['myMetadata']); $myUserMeta['myMetadata'] = array_values($myUserMeta['myMetadata']); $newToggle = "n"; $message = "Add to Favorites"; } else { // Add item to Favorites list $myUserMeta['myMetadata'][] = $postID; $newToggle = "y"; $message = "Remove from Favorites"; } // Prep for the response // Nonce for next request - unique with microtime string appended $newNonce = wp_create_nonce('myplugin_myaction_' . $postID . '_' . str_replace('.', '', gettimeofday(true))); $updateUserMeta = update_user_meta($userID, 'myMetadata', $myUserMeta); // Response to jQuery if($updateUserMeta === false) { $response['type'] = "error"; $response['theToggle'] = $toggle; $response['message'] = "Your Favorites could not be updated."; $response['newNonce'] = $newNonce; } else { $response['type'] = "success"; $response['theToggle'] = $newToggle; $response['message'] = $message; $response['newNonce'] = $newNonce; } // Process with Ajax, otherwise process with self if (! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { $response = json_encode($response); echo $response; } else { header("Location: " . $_SERVER["HTTP_REFERER"]); } exit(); } // End is_user_logged_in() } add_action('wp_ajax_myFavorites', 'toggleFavorites'); 

Мне действительно нужно подвергнуть сомнению аргументы в пользу получения нового nonce для каждого запроса ajax. Исходное значение nonce истечет, но его можно использовать более одного раза, пока оно не будет выполнено. Имея javascript, получая его через ajax, побеждает цель, особенно предоставляя ее в случае ошибки. (Цель nonces – небольшая защита для привязки действия с пользователем в течение временного интервала).

Я не должен упоминать другие ответы, но я новичок и не могу комментировать выше, поэтому в отношении опубликованного «решения» вы каждый раз получаете новый nonce, но не используете его в запросе. Разумеется, было бы сложно получить микросекунды одинаково каждый раз, чтобы соответствовать каждому новому носителю, созданному таким образом. PHP-код проверяет исходное значение nonce, а javascript предоставляет исходное значение nonce … поэтому он работает (потому что он еще не истек).