La funzione wp_get_current_user() non funziona nella callback dell'API REST

4 apr 2018, 23:08:41
Visualizzazioni: 18.1K
Voti: 17

Considera la seguente classe.

<?php
class MCQAcademy_Endpoint extends WP_REST_Controller {

    /**
     * Registra le route per gli oggetti del controller.
     */
    public function register_routes() {
        $version = '1';
        $namespace = 'custompath/v' . $version;
        $base = 'endpointbase';

        register_rest_route(
            $namespace,
            '/' . $base,
            array(
                array(
                    'methods'         => WP_REST_Server::READABLE,
                    'callback'        => array( $this, 'get_items' ),
                    'permission_callback' => array( $this, 'get_items_permissions_check' ),
                    'args'            => array(),
                )
            )
        );
    }

    /**
     * Ottiene gli elementi
     */
    public function get_items( $request ) {
        $rs = array(
            'data' => array(),
            'request' => array(
                'lang' => 'en',
            ),
        );

        $args = array();
        $items = get_posts( $args );

        foreach( $items as $item ) {
            $itemdata = $this->prepare_item_for_response( $item, $request );
            $rs['data'][] = $this->prepare_response_for_collection( $itemdata );
        }

        $rs['wp_get_current_user'] = wp_get_current_user(); // Non restituisce l'output atteso

        return new WP_REST_Response( $rs, 200 );
    }

    /**
     * Verifica se una richiesta ha accesso per ottenere gli elementi
     */
    public function get_items_permissions_check( $request ) {
        return true; // per renderlo leggibile da tutti
    }


    /**
     * Prepara l'elemento per l'operazione di creazione o aggiornamento
     */
    protected function prepare_item_for_database( $request ) {
        return $request;
    }

    /**
     * Prepara l'elemento per la risposta REST
     */
    public function prepare_item_for_response( $item, $request ) {
        $data = array(
            'ID' => $item->ID,
            'post_content' => wpautop($item->post_content),
            'post_title' => $item->post_title,
        );

        return $data;
    }

    /**
     * Ottiene i parametri di query per le collezioni
     */
    public function get_collection_params() {
        return array(
            'page'     => array(
                'description'        => 'Pagina corrente della collezione.',
                'type'               => 'integer',
                'default'            => 1,
                'sanitize_callback'  => 'absint',
            ),
            'per_page' => array(
                'description'        => 'Numero massimo di elementi da restituire nel set di risultati.',
                'type'               => 'integer',
                'default'            => 10,
                'sanitize_callback'  => 'absint',
            ),
            'search'   => array(
                'description'        => 'Limita i risultati a quelli che corrispondono a una stringa.',
                'type'               => 'string',
                'sanitize_callback'  => 'sanitize_text_field',
            ),
        );
    }

    // Registra il nostro server REST
    public function hook_rest_server(){
        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
    }
}

$myEndpoint = new MCQAcademy_Endpoint();
$myEndpoint->hook_rest_server();

Tutto funziona correttamente tranne quando si chiama la funzione wp_get_current_user() nella funzione get_items(), che restituisce un utente vuoto nonostante l'utente sia loggato nel sito web.

0
Tutte le risposte alla domanda 4
10
17

Essere loggato sul tuo sito web non significa che l'utente sia autenticato nella richiesta REST API, ecco perché non stai ottenendo l'utente corretto o un Id = 0

Dai un'occhiata ai metodi di autenticazione REST API nella documentazione:
https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

Per gli sviluppatori che effettuano richieste Ajax manuali, il nonce dovrà essere passato con ogni richiesta. L'API utilizza nonce con l'azione impostata su wp_rest. Questi possono poi essere passati all'API tramite il parametro dati _wpnonce (dati POST o nella query per le richieste GET), o tramite l'header X-WP-Nonce. Se non viene fornito alcun nonce, l'API imposterà l'utente corrente su 0, trasformando la richiesta in una richiesta non autenticata, anche se sei loggato in WordPress.

Per l'autenticazione remota consiglio il plugin JWT per iniziare rapidamente:

Oppure puoi usare quelli suggeriti nella documentazione:

5 apr 2018 00:24:57
Commenti

Ho bisogno dell'ID utente se l'utente è loggato nel sito. Poiché l'endpoint è aperto, non è necessario verificare i permessi.

Shah Alom Shah Alom
5 apr 2018 00:36:18

Questa riga restituisce sempre false anche se l'utente è loggato nel sito web. $rs['wp_get_current_user'] = is_user_logged_in() ? get_current_user_id() : false;

Shah Alom Shah Alom
5 apr 2018 00:46:36

Ciao @ShahAlom, come ho menzionato nella mia risposta, essere loggati nel sito web non è la stessa cosa che essere loggati nell'API REST, ti consiglio di dare un'occhiata ai metodi di autenticazione dell'API REST nella documentazione: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

Pabamato Pabamato
5 apr 2018 00:52:10

Per l'autenticazione tramite cookie: per gli sviluppatori che effettuano richieste Ajax manuali, il nonce dovrà essere passato con ogni richiesta. L'API utilizza nonce con l'azione impostata su wp_rest. Questi possono poi essere passati all'API tramite il parametro dati _wpnonce (sia nei dati POST che nella query per le richieste GET), o tramite l'header X-WP-Nonce. Se non viene fornito alcun nonce, l'API imposterà l'utente corrente a 0, trasformando la richiesta in una richiesta non autenticata, anche se si è loggati in WordPress.

Pabamato Pabamato
5 apr 2018 00:53:39

Per favore aggiorna la tua risposta con questo ultimo commento così che io possa accettarla come soluzione.

Shah Alom Shah Alom
5 apr 2018 23:02:23

Anche con l'autenticazione JWT, è comunque necessario passare l'header X-WP-Nonce per ottenere l'utente corrente, ad esempio se hai bisogno di accedere a users/me restituisce comunque 401, lo stesso per query users by role, e così via. Le richieste AJAX "non manuali" sono "speciali" solo perché il client API ufficiale include quell'header, ma nient'altro.

Jesús Franco Jesús Franco
7 apr 2018 08:14:18

@JesúsFranco dalla mia esperienza usando il plugin JWT, non è necessario inviare alcun header aggiuntivo, basta l'header Authorization con il token generato precedentemente per l'autenticazione, /wp-v2/users/me funziona per me

Pabamato Pabamato
8 apr 2018 07:22:41

Hmmm, questo mi fa pensare a cosa potrebbe essere diverso da setup a setup, la versione del plugin? Del core?

Jesús Franco Jesús Franco
9 apr 2018 21:36:53

Riflettendo su quali potrebbero essere le differenze, ho osservato due tipi di richieste che vengono proibite senza l'uso del nonce, e permesse senza di esso, allo stesso modo. La differenza che ho notato è l'ambiente della richiesta. Le richieste attraverso uno script PHP sono permesse con il solo Token sufficiente, le richieste attraverso il browser no senza il nonce.

Jesús Franco Jesús Franco
9 apr 2018 21:48:20

@JesúsFranco hai ragione, per le richieste lato server (nel mio caso), il token sarà sufficiente, per chiamate AJAX lato client/manuali, devi includere il nonce per evitare CSRF, non c'è bisogno di includere il token dato che hai già un utente loggato e il lato client è basato sui cookie

Pabamato Pabamato
10 apr 2018 22:40:45
Mostra i restanti 5 commenti
0

Se una chiamata API arriva senza un nonce impostato, WordPress deautenticherà la richiesta, terminando l'utente corrente e rendendo la richiesta non autenticata. Questa protezione CSFR è automatica e non devi fare nulla per farla funzionare oltre a includere il nonce.

Il modo per risolvere questo problema è includere un nonce. Qualcosa come questo:

$_wpnonce = wp_create_nonce( 'wp_rest' );
echo "<input type = 'hidden' name = '_wpnonce' value = '$_wpnonce' />";

E includerlo nella tua richiesta API. Il nonce deve essere chiamato "_wpnonce" e l'azione deve essere "wp_rest" per far funzionare l'API. Può essere inserito nell'URL o nel corpo del post.

6 feb 2020 20:27:55
3

Per avere un esempio di codice completamente funzionante, ecco un plugin di esempio su come recuperare l'ID dell'utente corrente via REST.

my-plugin.php

class MyPlugin {

  public function __construct() {

    add_action('wp_enqueue_scripts', [$this, 'scripts']);
    add_action('rest_api_init', [$this, 'rest']);
  }

  function scripts() {

    // Aggiunge JS.
    wp_enqueue_script('my-plugin', plugin_dir_url(__FILE__) . 'js/scripts.js', ['jquery'], NULL, TRUE);
    // Passa il nonce al JS.
    wp_localize_script('my-plugin', 'MyPluginSettings', [
      'nonce' => wp_create_nonce('wp_rest'),
    ]);
  }

  function rest() {

    // Registra la rotta.
    register_rest_route('my-plugin/v1', '/uid', [
      'methods'  => WP_REST_Server::READABLE,
      'callback' => [$this, 'rest_callback'],
    ]);
  }

  function rest_callback($data) {

    // Ottieni l'ID dell'utente corrente.
    $data = [
      'uid' => get_current_user_id(),
    ];

    $response = new WP_REST_Response($data, 200);
    // Imposta gli header.
    $response->set_headers(['Cache-Control' => 'must-revalidate, no-cache, no-store, private']);

    return $response;
  }

}

new MyPlugin();

js/scripts.js

(function($) {

  $(document).ready(function() {

    var settings = MyPluginSettings;

    $.ajax({
      url: '/wp-json/my-plugin/v1/uid',
      method: 'GET',
      beforeSend: function(xhr) {
        xhr.setRequestHeader('X-WP-Nonce', settings.nonce);
      }
    }).done(function(response) {

      // Restituirà il tuo UID.
      console.log(response);
    });

  });

})(jQuery);

Risorsa: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

26 feb 2019 12:40:20
Commenti

Non capisco come 'MyPluginSettings' venga passato allo script. Cose da considerare: attualmente non sto usando JQuery ma JavaScript "vanilla" ... stavo inserendo il mio script nei tag html <script> quando necessario, ma ho provato a includerli con wp_enqueue_script e ho avuto lo stesso problema.

TTT TTT
10 apr 2019 21:00:47

Ho risolto il problema, avevo scritto un percorso errato quando ho adattato per wp_enqueue_script(). Inoltre, avevo più script e ho appena capito che il primo parametro di wp_enqueue_script() non è specifico del plug-in ma è specifico dello script (forse dovresti cambiare 'my-plugin' nella tua risposta perché può creare confusione).

TTT TTT
10 apr 2019 21:12:41

@TTT – Sostituirlo con my-script per esempio? Sì, dovrebbe essere semplicemente un nome handle univoco, che spesso contiene il nome del plugin o del tema.

norman.lol norman.lol
10 apr 2019 23:38:52
0

wp_get_current_user() non funziona perché il tuo utente non è impostato correttamente. Vedi il codice qui sotto in /wp-includes/user.php per riferimento.

if ( ! empty( $current_user ) ) {
    if ( $current_user instanceof WP_User ) {
        return $current_user;
    }

    // Aggiorna stdClass a WP_User
    if ( is_object( $current_user ) && isset( $current_user->ID ) ) {
        $cur_id       = $current_user->ID;
        $current_user = null;
        wp_set_current_user( $cur_id );
        return $current_user;
    }

    // $current_user ha un valore non valido. Forza a WP_User con ID 0.
    $current_user = null;
    wp_set_current_user( 0 );
    return $current_user;
}

if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
    wp_set_current_user( 0 );
    return $current_user;
}

Tutto quello che devi fare è chiamare wp_set_current_user( {user_id} ) all'inizio della tua API.

17 apr 2019 09:05:38