Функция wp_get_current_user() не работает в callback-функции REST API
Рассмотрим следующий класс.
<?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()
, которая возвращает пустого пользователя, даже если пользователь авторизован
на сайте.

Тот факт, что пользователь вошел на ваш сайт, не означает, что он аутентифицирован для запросов к 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 для быстрого старта:
Или вы можете использовать варианты, предложенные в документации:

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

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

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

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

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

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

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

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

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

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

Если 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-запроса.

Вот полностью рабочий пример кода для получения 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/

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

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

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.
