Her Ajax talebi için benzersiz bir nonce nasıl edinilir?


11

Wordpress'in sonraki Ajax istekleri için benzersiz bir nosyonu yeniden oluşturması hakkında birkaç tartışma gördüm, ancak benim hayatım için Wordpress'i yapamıyorum - her ne zaman yeni bir şey olacağını düşündüğümde nonce, Wordpress'ten aynı nonce'yi geri alıyorum. WP'nin nonce_life kavramını anladım ve hatta başka bir şeye ayarladım, ama bu bana yardımcı olmadı.

Yerelleştirme yoluyla başlıktaki JS nesnesindeki nonce üretmiyorum - bunu görüntü sayfamda yapıyorum. Ajax isteğini işlemek için sayfamı alabilirim, ancak geri aramada WP'den yeni bir nonce istediğimde, aynı nonce'yi geri alıyorum ve ne yaptığımı bilmiyorum ... Nihayetinde yapmak istiyorum her sayfada ekleme / kaldırma yeteneğine sahip birden çok öğe olabilmesi için bunu uzatın - bu yüzden bir sayfadan birden fazla sonraki Ajax isteğine izin verecek bir çözüme ihtiyacım var.

(Ve tüm bu işlevselliği bir eklentiye koyduğumu söylemeliyim, bu yüzden ön uç "görüntü sayfası" aslında eklentiyle birlikte gelen bir işlev ...)

function.php: localize, ama burada bir nonce oluşturmuyorum

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

JS aranıyor:

$("#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 alınıyor:

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();
}

Ön uç PHP görüntüleme işlevi, bunlar arasında:

$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>';

Bu noktada olurdu gerçekten minnettar herhangi Her yeni Ajax istek için eşsiz bir nonce yeniden oluşturmak için WP almakla ipuçları veya işaretçiler ...


GÜNCELLEME: Sorunumu çözdüm. Yukarıdaki kod parçacıkları geçerlidir, ancak ben sonraki Ajax istekleri benzersiz olduğundan emin olmak için bir mikrosaniye dize eklemek için PHP geri arama $ newNonce oluşturma değiştirdi.


Bir itibaren çok kısa bir görünüm: Eğer (ekranda) bunu aldıktan sonra Sen nonce yaratıyorsun? Yerelleştirme çağrısı sırasında neden bunu oluşturmuyorsunuz?
kaiser

JQuery, bir # myelement bağlantısındaki "data-nonce" özelliğindeki ilk nonce'yi kullanıyor ve fikir, sayfanın Ajax veya kendisi tarafından işlenebilmesidir. Bana lokalize çağrı ile bir kez nonce oluşturma onu JS olmayan işleme hariç tutacak gibi görünüyordu, ama bu konuda yanlış olabilir. Her iki şekilde de Wordpress bana aynı nonce geri veriyor ...
Tim

Ayrıca: Nonce değerini yerelleştir çağrısına koymak, bir sayfada her öğenin bir Ajax isteği için benzersiz bir nonce'ye sahip olabileceği birden fazla öğe olmasını engellemez mi?
Tim

Localize içinde nonce oluşturmak, o komut dosyası için oluşturur ve kullanılabilir hale getirir. Ancak, sınırsız sayıda diğer (anahtar adlı) yerelleştirilen değerleri ayrı nonce'lerle de ekleyebilirsiniz.
Kaiser

Çözdüyseniz, yanıtınızı göndermeniz ve "kabul edildi" olarak işaretlemeniz önerilir. Sitenin düzenli tutulmasına yardımcı olacaktır. Sadece kodunuzu karıştırıyordum ve birkaç şey benim için çalışmıyor, bu yüzden çözümünüzü göndermeniz için bu isteği iki katına çıkarın.
s_ha_dum

Yanıtlar:


6

Aşağıda, sonraki Ajax istekleri için benzersiz nosyonlar üretme sorununun ötesine geçen kendi sorumun çok uzun bir cevabı var. Bu, yanıt amacıyla genel yapılan bir "sık kullanılanlara ekle" özelliğidir (benim özelliğim, kullanıcıların fotoğraf eklerinin posta kimliklerini sık kullanılanlar listesine eklemesine olanak tanır, ancak bu, güvenilen diğer çeşitli özellikler için geçerli olabilir ajax). Bunu bağımsız bir eklenti olarak kodladım ve eksik birkaç öğe var - ancak özelliği çoğaltmak istiyorsanız bu özeti sağlamak için yeterli ayrıntı olmalıdır. Tek bir gönderi / sayfada çalışacak, ancak gönderi listelerinde de çalışacaktır (örneğin, Ajax aracılığıyla satır içi sık kullanılanlara öğe ekleyebilir / kaldırabilirsiniz ve her gönderinin her Ajax isteği için kendine özgü bir nonce'si olacaktır). Unutmayın ki orada '

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 (Kaldırılabilecek birçok hata ayıklama öğesi)

$(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
});

İşlevler (ön uç ekran ve Ajax işlemi)

Sık Kullanılan Ekle / Kaldır bağlantısını çıkarmak için, sayfanızda / yayında şu yolla arayın:

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

Ön uç görüntüleme fonksiyonu:

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 eylem fonksiyonu:

/**
* 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');

3

Gerçekten her ajax isteği için yeni bir nonce alma arkasındaki nedeni sorgulamak zorunda. Orijinal nonce'nin süresi dolar, ancak bitene kadar bir kereden fazla kullanılabilir. Javascript'in ajax aracılığıyla almasını sağlamak, özellikle bir hata durumunda sağlamak amacını bozar. (Nonces'in amacı, bir zaman dilimi içinde bir eylemi bir kullanıcıyla ilişkilendirmek için biraz güvenlik sağlamaktır.)

Diğer cevaplardan bahsetmem gerekiyor, ama yeniyim ve yukarıda yorum yapamam, bu yüzden yayınlanan "çözüm" ile ilgili olarak, her seferinde yeni bir nonce alıyorsunuz ancak istekte kullanmıyorsunuz. Mikrosaniyelerin her seferinde bu şekilde oluşturulan her yeni nonce ile eşleşmesi kesinlikle zor olacaktır. PHP kodu orijinal nonce karşı doğrulanıyor ve javascript orijinal nonce sağlıyor ... bu yüzden çalışır (henüz süresi dolmadı çünkü).


1
Sorun şu ki, nonce kullanıldıktan sonra sona erer ve her seferinde ajax fonksiyonunda -1 döndürür. PHP'de bir formun bölümlerini doğrular ve yazdırmak için hata döndürürseniz, bu sorun olur. Form nonce kullanıldı, ancak alanların php doğrulamasında hata oluştu ve form tekrar gönderildiğinde, bu kez doğrulanamıyor ve check_ajax_referer-1, bu da istediğimiz şey değil!
Solomon Closson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.