如何为每个AJAX请求获取唯一的现时值?

时间:2013-01-29 作者:Tim

我看到过一些关于让Wordpress为后续Ajax请求重新生成唯一nonce的讨论,但就我而言,我实际上无法让Wordpress这样做——每次我请求我认为应该是新nonce的内容时,我都会从Wordpress那里得到相同的nonce。我理解WP的nonce\\u生活的概念,甚至将其设置为其他内容,但这对我没有帮助。

我不会通过本地化在标题中的JS对象中生成nonce,而是在我的显示页面上生成。我可以让我的页面处理Ajax请求,但当我在回调中从WP请求新的nonce时,我会得到相同的nonce,我不知道我做错了什么。。。最终,我想扩展它,以便页面上可以有多个项目,每个项目都可以添加/删除——因此,我需要一个允许从一个页面发出多个后续Ajax请求的解决方案。

(我应该说我已经把所有这些功能都放到了一个插件中,所以前端的“显示页面”实际上是插件中包含的一个功能……)

功能。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();
}
前端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为每个新的Ajax请求重新生成一个唯一的nonce的任何线索或指针。。。

更新:我已经解决了我的问题。上面的代码片段是有效的,但是我更改了PHP回调中的$newNonce创建,添加了一个微秒字符串,以确保它在后续Ajax请求中是唯一的。

2 个回复
最合适的回答,由SO网友:Tim 整理而成

这里有一个关于我自己问题的很长的答案,它不仅仅是解决为后续Ajax请求生成唯一nonce的问题。这是一个“添加到收藏夹”功能,出于回答的目的,它是通用的(我的功能允许用户将照片附件的帖子ID添加到收藏夹列表中,但这可能适用于依赖Ajax的其他各种功能)。我将其编码为一个独立的插件,缺少了一些项目——但如果您想复制该功能,这应该足够详细,可以提供要点。它可以在单个帖子/页面上运行,但也可以在帖子列表中运行(例如,您可以通过Ajax在线向收藏夹添加/删除项目,每个帖子对于每个Ajax请求都有自己独特的nonce)。请记住,可能有一种更有效和/或更优雅的方法可以做到这一点,目前这种方法仅适用于Ajax——我还没有费心处理非Ajax$\\u 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
});

Functions (front-end display & Ajax action)

要输出“添加/删除收藏夹”链接,只需通过以下方式在页面/帖子上调用它:

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\');

SO网友:Joy Reynolds

我真的不得不质疑为每个ajax请求获取新nonce背后的原因。原始nonce将过期,但在过期之前可以多次使用。让javascript通过ajax接收到它,这是行不通的,尤其是在错误情况下提供它。(nonces的目的是为了在一个时间范围内将操作与用户关联起来,提供一点安全性。)

我不想提及其他答案,但我是新来的,无法在上面发表评论,因此对于发布的“解决方案”,您每次都会得到一个新的nonce,但在请求中没有使用它。要使微秒每次都相同,以匹配以这种方式创建的每个新的nonce,这无疑是一件棘手的事情。PHP代码正在根据原始nonce进行验证,javascript正在提供原始nonce。。。所以它是有效的(因为它还没有过期)。

结束

相关推荐

WordPress Ajax Problems

更新我使用以下curl命令启动它:curl -H \"Accept: application/json\" -X POST http://localhost/wordpress/wp-admin/admin-ajax.php -d \"action=vixo_wordpress_signon&data=banjometer\" --trace -不太确定如何让它接受json,但嘿。。。最初的问题是,我是WordPress的新手,在使用Ajax时遇到了一些问题,但在理解WordPress如何加载PH