我尝试使用cookie authentication使用Fetch API进行WordPress REST API访问,但是auth失败并出现以下错误。
403:Cookie Nonce无效
我使用以下脚本连接到API。
let request = new XMLHttpRequest();
request.open('POST', WPAPI.root + 'my-endpoint/upload/', true);
request.setRequestHeader('X-WP-Nonce', WPAPI.nonce);
request.setRequestHeader('Content-Type', 'application/json');
request.send(JSON.stringify(data));
当我从使用Fetch切换到XMLHttpRequest时,它按预期工作。
{{1}}
在Fetch方法中发送标头的方式是否有问题?
答案 0 :(得分:2)
WordPress nonce身份验证需要使用Cookie,默认情况下,Fetch不会发送这些身份验证。您可以使用凭据选项来完成此任务:
fetch(endpoint, {
credentials: 'same-origin'
})
答案 1 :(得分:0)
从 4 年前看到我的帖子寻找同样的问题:) 这解决了问题。
const response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce' : my_var.nonce
},
body: JSON.stringify(data),
});
const content = await response.json();
console.log(content);
答案 2 :(得分:0)
晚了,但可能对其他读者有所帮助,因为我根据这个问题专门为 fetch() 承诺添加了代码。
据我所知,WordPress 在其 cookie 中自动使用 nonce。
WordPress:版本 5.7.2
PHP:7.4 版
主机:hostmonster.com
客户端:Windows 10
浏览器:在 Chrome、Firefox、甚至 Edge 上测试过 ? 工作
代码(安装主题的function.php中的PHP代码):
add_action('rest_api_init', function() {
/**
* Register here your custom routes for your CRUD functions
*/
register_rest_route( 'my-endpoint/v1', '/upload/', array(
array(
'methods' => WP_REST_Server::READABLE, // = 'GET'
'callback' => 'get_data',
// Always allow, as an example
'permission_callback' => '__return_true'
),
array(
'methods' => WP_REST_Server::CREATABLE, // = 'POST'
'callback' => 'create_data',
// Here we register our permissions callback
// The callback is fired before the main callback to check if the current user can access the endpoint
'permission_callback' => 'prefix_get_private_data_permissions_check',
),
));
});
// The missing part:
// Add your Permission Callback function here, that checks for the cookie
// You should define your own 'prefix_' name, though
function prefix_get_private_data_permissions_check() {
// Option 1: Password Protected post or page:
// Restrict endpoint to browsers that have the wp-postpass_ cookie.
if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] )) {
return new WP_Error( 'rest_forbidden', esc_html__( 'OMG you can not create or edit private data.', 'my-text-domain' ), array( 'status' => 401 ) );
};
// Option 2: Authentication based on logged-in user:
// Restrict endpoint to only users who have the edit_posts capability.
if ( ! current_user_can( 'edit_posts' ) ) {
return new WP_Error( 'rest_forbidden', esc_html__( 'OMG you can not create or edit private data.', 'my-text-domain' ), array( 'status' => 401 ) );
};
// This is a black-listing approach. You could alternatively do this via white-listing, by returning false here and changing the permissions check.
return true;
};
function create_data() {
global $wpdb;
$result = $wpdb->query(...);
return $result;
}
function get_data() {
global $wpdb;
$data = $wpdb->get_results('SELECT * from `data`');
return $data;
}
确保在您的 HTTP 请求中包含您的 HTML 页面中的 credentials: 'same-origin'
,如以上先前的回答和评论中所述。
代码(带有内联 <script> ... </script>
的HTML):
<script>
// Here comes the REST API part:
// HTTP requests with fetch() promises
function getYourData() {
let url = 'https://example.com/wp-json/my-endpoint/v1/upload/';
fetch(url, {
method: 'GET',
credentials: 'same-origin', // <-- make sure to include credentials
headers:{
'Content-Type': 'application/json',
'Accept': 'application/json',
//'Authorization': 'Bearer ' + token <-- not needed, WP does not check for it
}
}).then(res => res.json())
.then(response => get_success(response))
.catch(error => failure(error));
};
function insertYourData(data) {
let url = 'https://example.com/wp-json/my-endpoint/v1/upload/';
fetch(url, {
method: 'POST',
credentials: 'same-origin', // <-- make sure to include credentials
headers:{
'Content-Type': 'application/json',
'Accept': 'application/json',
//'Authorization': 'Bearer ' + token <-- not needed, WP does not check for it
},
body: JSON.stringify(data)
}).then(res => res.json())
.then(response => create_success(response))
.catch(error => failure(error));
};
// your Success and Failure-functions:
function get_success(json) {
// do something here with your returned data ....
console.log(json);
};
function create_success(json) {
// do something here with your returned data ....
console.log(json);
};
function failure(error) {
// do something here ....
console.log("Error: " + error);
};
</script>
'Authorization': 'Bearer ' + token
是否需要在 HTTP 请求头中?
经过一些测试后,我意识到权限回调中的if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] )) { ...
不仅会检查客户端浏览器上是否设置了Cookie,而且似乎还会检查其值( JWT 令牌)。
因为我像我的初始代码一样反复检查,传递错误令牌,消除 cookie,或保持会话打开但在后端更改站点的密码(因此 WordPress 会创建一个新令牌,因此值为 set wp_postpass_
cookie 会改变)并且所有测试都正确进行 - REST API 被阻止,不仅验证 cookie 的存在,还验证其价值(这很好 - 谢谢 WordPress 团队)。 >
来源:
我在 FAQ section 中找到了有关上述想法的以下资源:
因为 WordPress REST API 不验证 Origin 标头 传入请求,因此可以访问公共 REST API 端点 从任何网站。这是一个有意的设计决定。
然而,WordPress 有一个现有的 CSRF 保护机制, 使用随机数。
根据我目前的测试,WP 身份验证方式运行良好。
来自 WordPress REST API 手册的另外 2 个来源:
REST API Handbook / Extending the REST API / Routes and Endpoints
REST API Handbook / Extending the REST API / Adding Custom Endpoints
和 1 个源表单 WordPress 代码参考关于 rest_cookie_check_errors()
函数:
Reference / Functions / rest_cookie_check_errors()
对于那些对我的发现的完整故事感兴趣的人,请点击我的主题链接,其中包含答案、代码片段和其他发现。