特级教程:如何利用wp的Nonce或者password application来实现插件rest api发送的验证功能(PHP-AJAX版)

Published
2023-02-19
浏览次数 :  172

这两天我遇到了一个十分不解的事情,我编写插件的时候,需要设置接口的访问权限,防止滥用,因为是post提交,如果滥用对数据也会产生极大的风险。

我第一个想到的是用nonce.

我先在插件里通过wp_localize_script创建nonce,

将这个放置在wp_enqueue_script的最后,

wp_localize_script( 'plugin_index', 'ha_submission',array(
    'site_url'  => home_url(),
    'nonce' => wp_create_nonce('plugin_ha_submission')
  ) );

然后我在发送ajax的时候,设置beforeSend来添加发送时候的headers里面的X-WP-Nonce.

$.ajax({
    url:ha_submission.site_url + '/wp-json/ha_submission/v1/submission',

    beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce',ha_submission.nonce)

}, 
  method:'POST',
  data:$(this).serialize(),

    success: res => { }

当然我事先是先添加了自定义的路由了的,并且路由访问都正常的。

然而问题来了,这个beforeSend里面的nonce怎样都不能用, 永远返回的是cookie_invalid_nonce 错误,返回403。

足足折腾了两天。

重磅更新

用nonce可以通过authentication,但是这个nonce的action必须是wp_rest, 也就是说,在本地化wp_localize_script里面,nonce必须设置 nonce=>wp_create_nonce(‘wp_rest)

官方网址:https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

利用nonce写法

<?php
wp_localize_script( 'wp-api', 'wpApiSettings', array(
    'root' => esc_url_raw( rest_url() ),
    'nonce' => wp_create_nonce( 'wp_rest' )
) );

options.beforeSend = function(xhr) {
    xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce);

    if (beforeSend) {
        return beforeSend.apply(this, arguments);
    }
};

$.ajax( {
    url: wpApiSettings.root + 'wp/v2/posts/1',
    method: 'POST',
    beforeSend: function ( xhr ) {
        xhr.setRequestHeader( 'X-WP-Nonce', wpApiSettings.nonce );
    },
    data:{
        'title' : 'Hello Moon'
    }
} ).done( function ( response ) {
    console.log( response );
} );

然后我突然想用wp两年前刚出的application password功能。

问题一下子迎刃而解。

首先定义用户名和密码,用户名是你网站管理员用户名,密码是你在后台用户那里生成的application password.

const username = '';
  const password = '';

然后通过base64 基本验证来进行加密: 也是使用必须的要求:

const credentials = `${username}:${password}`;
const encodedCredentials = btoa(credentials);

然后在ajax发送数据传递参数那里,设置headers

headers: {
    'Authorization': `Basic ${encodedCredentials}`
  },

完整代码如下:用户名和密码需要修改你自己的 (注意这里btoa用法) beforeSend用法 我还没测试过:

const username = '';
  const password = '';
  const credentials = `${username}:${password}`;
const encodedCredentials = btoa(credentials);
  $("#submission_form").on('submit',function(e) {
  e.preventDefault();
  
  //form validation
  if ($(this)[0].checkValidity() && $("#verify").val() == 6547) {
    console.log(ha_submission.nonce);
    $.ajax({
    url:ha_submission.site_url + '/wp-json/ha_submission/v1/submission',

    headers: {
    'Authorization': `Basic ${encodedCredentials}`
  },
  method:'POST',
  data:$(this).serialize(),

    success: res => {
      
      console.log(res);

      $(this)[0].reset();

      $("#formAlert").html("<h2 style='color:green'>成功提交信息,页面即将跳转</h2>");

      setTimeout(function (argument) {
        // body... 
         $("#formAlert").html('');
        window.location = ha_submission.site_url;
      },1500);
      
      console.log('success');
    },
    error: err => {
      $("#formAlert").html("<h2 style='color:red'>内部服务器错误</h2>")
      console.log(err.responseText); 
      console.log('Fail in xmdn');
    }
  })


  } else {
    $("#verifyAlert").html("<p style='color:red'>答案不正确请重新输入</p>");
    setTimeout(function(){$("#verifyAlert").html('')},1500);
    console.log('请检查表单输入!');
  }

});

BINGGO。比用nonce好用多了。 这样别人就无法通过这个接口来实现post提交了。因为实现Post提交的接口已经被我拦截了。至于Nonce 为什么不能用。我还在研究。照理说代码已经完美了。

当然最后也可以在接口php那里post提交那里进行后端验证,比方说验证是否是本网站发出的 不然可以拦截请求。

后端验证的方法实现必须要这个加密才能访问这个POST接口,一切都完美了

<?php

if (!isset($_SERVER['HTTP_AUTHORIZATION']) || empty($_SERVER['HTTP_AUTHORIZATION'])) {
  // Authorization header is not set or is empty, handle the error
  header('HTTP/1.1 401 Unauthorized');
  echo 'Authorization header is missing or empty';
  exit;
}

// Authorization header is set, decode the credentials
$auth_header = $_SERVER['HTTP_AUTHORIZATION'];
list($auth_type, $auth_data) = explode(' ', $auth_header, 2);

if ($auth_type !== 'Basic') {
  // Unsupported authorization type, handle the error
  header('HTTP/1.1 401 Unauthorized');
  echo 'Unsupported authorization type';
  exit;
}

$auth_data = base64_decode($auth_data);
list($username, $password) = explode(':', $auth_data, 2);

// Now you can use the $username and $password to authenticate the user
// and perform the necessary actions with the WordPress REST API

?>

检测nonce是这样的:

if (!isset($_POST[‘subscriber_nonce’]) && !wp_verify_nonce(‘subscriber_nonce’,basename(FILE))) {
return $post_id;
}

用beforeSend验证“:

 beforeSend: function(xhr) {
    xhr.setRequestHeader("Authorization", `Basic ${encodedCredentials}`);
  },

Top