Функция wp_get_current_user() не работает в callback-функции REST API

4 апр. 2018 г., 23:08:41
Просмотры: 18.1K
Голосов: 17

Рассмотрим следующий класс.

<?php
class MCQAcademy_Endpoint extends WP_REST_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(),
                )
            )
        );
    }

    /**
     *
     */
    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(); // Не возвращает ожидаемый результат

        return new WP_REST_Response( $rs, 200 );
    }

    /**
     * Проверяет права доступа для получения элементов
     */
    public function get_items_permissions_check( $request ) {
        return true; // сделано общедоступным
    }


    /**
     * Подготавливает элемент для операций создания или обновления
     */
    protected function prepare_item_for_database( $request ) {
        return $request;
    }

    /**
     * Подготавливает элемент для 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;
    }

    /**
     * Получает параметры запроса для коллекций
     */
    public function get_collection_params() {
        return array(
            'page'     => array(
                'description'        => 'Текущая страница коллекции.',
                'type'               => 'integer',
                'default'            => 1,
                'sanitize_callback'  => 'absint',
            ),
            'per_page' => array(
                'description'        => 'Максимальное количество элементов в результирующем наборе.',
                'type'               => 'integer',
                'default'            => 10,
                'sanitize_callback'  => 'absint',
            ),
            'search'   => array(
                'description'        => 'Ограничить результаты теми, что соответствуют строке.',
                'type'               => 'string',
                'sanitize_callback'  => 'sanitize_text_field',
            ),
        );
    }

    // Регистрируем наш REST-сервер
    public function hook_rest_server(){
        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
    }
}

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

Всё работает корректно, за исключением вызова функции wp_get_current_user() в функции get_items(), которая возвращает пустого пользователя, даже если пользователь авторизован на сайте.

0
Все ответы на вопрос 4
10
17

Тот факт, что пользователь вошел на ваш сайт, не означает, что он аутентифицирован для запросов к REST API. Именно поэтому вы получаете некорректного пользователя или Id = 0.

Рекомендуем ознакомиться с методами аутентификации REST API в документации:
https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

Для разработчиков, выполняющих ручные Ajax-запросы, необходимо передавать nonce с каждым запросом. API использует nonce с действием, установленным в wp_rest. Их можно передать в API через параметр данных _wpnonce (либо в POST-данных, либо в запросе для GET-запросов) или через заголовок X-WP-Nonce. Если nonce не предоставлен, API установит текущего пользователя в 0, превратив запрос в неаутентифицированный, даже если вы вошли в WordPress.

Для удаленной аутентификации рекомендуем плагин JWT для быстрого старта:

Или вы можете использовать варианты, предложенные в документации:

5 апр. 2018 г. 00:24:57
Комментарии

Мне нужен идентификатор пользователя, если он авторизован на сайте. Поскольку конечная точка открыта, проверка прав доступа не требуется.

Shah Alom Shah Alom
5 апр. 2018 г. 00:36:18

Эта строка всегда возвращает false, даже если пользователь авторизован на сайте. $rs['wp_get_current_user'] = is_user_logged_in() ? get_current_user_id() : false;

Shah Alom Shah Alom
5 апр. 2018 г. 00:46:36

Привет @ShahAlom, как я уже упоминал в своем ответе, авторизация на сайте отличается от авторизации в REST API. Пожалуйста, ознакомьтесь с методами аутентификации REST API в документации: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

Pabamato Pabamato
5 апр. 2018 г. 00:52:10

Для аутентификации с помощью cookies: разработчикам, выполняющим ручные Ajax-запросы, необходимо передавать nonce с каждым запросом. API использует nonce с действием, установленным в wp_rest. Затем их можно передать в API через параметр данных _wpnonce (либо в POST-данных, либо в запросе для GET-запросов) или через заголовок X-WP-Nonce. Если nonce не предоставлен, API установит текущего пользователя в 0, превращая запрос в неаутентифицированный, даже если вы вошли в WordPress.

Pabamato Pabamato
5 апр. 2018 г. 00:53:39

Пожалуйста, обновите ваш ответ с этим последним комментарием, чтобы я мог принять его как решение.

Shah Alom Shah Alom
5 апр. 2018 г. 23:02:23

Даже с аутентификацией JWT вам все равно нужно передавать заголовок X-WP-Nonce для получения текущего пользователя. Например, если вам нужно получить доступ к users/me, все равно возвращается 401, то же самое для запроса пользователей по роли и так далее. "Не ручные" AJAX-запросы являются "особыми" только потому, что официальный клиент API включает этот заголовок, но ничего больше.

Jesús Franco Jesús Franco
7 апр. 2018 г. 08:14:18

@JesúsFranco по моему опыту использования плагина JWT, нет необходимости отправлять какие-либо дополнительные заголовки, достаточно только заголовка Authorization с ранее сгенерированным токеном для аутентификации. /wp/v2/users/me работает у меня

Pabamato Pabamato
8 апр. 2018 г. 07:22:41

Хмм, это заставляет задуматься, что может отличаться в разных настройках - версия плагина? Версия ядра?

Jesús Franco Jesús Franco
9 апр. 2018 г. 21:36:53

Размышляя о возможных различиях, я заметил два типа запросов, которые запрещались без использования nonce, и также разрешались без него. Разница, которую я заметил, заключается в среде выполнения запроса. Запросы через PHP-скрипт разрешаются при наличии только токена, а запросы через браузер - нет, без nonce.

Jesús Franco Jesús Franco
9 апр. 2018 г. 21:48:20

@JesúsFranco вы правы, для запросов на стороне сервера (как в моем случае) токена будет достаточно, для клиентских/ручных ajax-запросов необходимо включать nonce для защиты от CSRF, нет необходимости включать токен, так как у вас уже есть авторизованный пользователь и клиентская часть работает на основе cookies

Pabamato Pabamato
10 апр. 2018 г. 22:40:45
Показать остальные 5 комментариев
0

Если API-вызов поступает без установленного одноразового кода (nonce), WordPress деаутентифицирует запрос, удаляя текущего пользователя и делая запрос неаутентифицированным. Эта защита от CSFR (межсайтовой подделки запроса) работает автоматически, и вам не нужно ничего делать для её работы, кроме как включить одноразовый код.

Решение заключается в добавлении одноразового кода. Например, так:

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

И затем включить его в ваш API-запрос. Одноразовый код должен называться "_wpnonce", а действие должно быть "wp_rest" для корректной работы API. Его можно передать в URL или в теле POST-запроса.

6 февр. 2020 г. 20:27:55
3

Вот полностью рабочий пример кода для получения ID текущего пользователя через REST API.

my-plugin.php

class MyPlugin {

  public function __construct() {

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

  function scripts() {

    // Подключаем JS
    wp_enqueue_script('my-plugin', plugin_dir_url(__FILE__) . 'js/scripts.js', ['jquery'], NULL, TRUE);
    // Передаём nonce в JS
    wp_localize_script('my-plugin', 'MyPluginSettings', [
      'nonce' => wp_create_nonce('wp_rest'),
    ]);
  }

  function rest() {

    // Регистрируем маршрут
    register_rest_route('my-plugin/v1', '/uid', [
      'methods'  => WP_REST_Server::READABLE,
      'callback' => [$this, 'rest_callback'],
    ]);
  }

  function rest_callback($data) {

    // Получаем ID текущего пользователя
    $data = [
      'uid' => get_current_user_id(),
    ];

    $response = new WP_REST_Response($data, 200);
    // Устанавливаем заголовки
    $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) {

      // Вернёт ваш UID
      console.log(response);
    });

  });

})(jQuery);

Источник: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

26 февр. 2019 г. 12:40:20
Комментарии

Я не понимаю, как 'MyPluginSettings' передается в скрипт. Возможные моменты для рассмотрения: в настоящее время я не использую JQuery, а "чистый" JavaScript ... Я вставлял свой скрипт в HTML-теги <script>, когда это было нужно, но попробовал подключать их через wp_enqueue_script и столкнулся с той же проблемой.

TTT TTT
10 апр. 2019 г. 21:00:47

У меня получилось это заработать. Я указал неправильный путь при адаптации для wp_enqueue_script(). Также у меня было несколько скриптов, и я только что понял, что первый параметр wp_enqueue_script() относится не конкретно к плагину, а к скрипту (возможно, вам стоит заменить 'my-plugin' в вашем ответе, потому что это вводит в заблуждение).

TTT TTT
10 апр. 2019 г. 21:12:41

@TTT – Заменить на my-script, например? Да, это должно быть просто какое-то уникальное имя (handle), часто содержащее название плагина или темы.

norman.lol norman.lol
10 апр. 2019 г. 23:38:52
0

wp_get_current_user() не работает, потому что ваш пользователь не установлен корректно. Для справки смотрите код ниже в /wp-includes/user.php.

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

    // Обновление stdClass до 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 содержит некорректное значение. Принудительно устанавливаем WP_User с 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;
}

Всё, что вам нужно сделать — это вызвать wp_set_current_user( {user_id} ) в начале вашего API.

17 апр. 2019 г. 09:05:38