在定制路线上强制授权

时间:2019-05-22 作者:Mr.Smithyyy

我正在wordpress主题中设置多个自定义api路由,我希望这需要一个JWT令牌才能访问。

因此,我按照这些步骤获得了WP-restapi插件的JWT身份验证,并且我能够成功地获得一个令牌并在默认路由上使用它。

然而,在自定义路由上不需要它,如果未指定授权标头,我将成功地从路由获取数据。如果我使用错误的承载令牌提供授权,那么JWT插件将启动并返回错误令牌,但这意味着只要没有定义授权头,就可以自由使用路由。

以下是我的自定义路线:

add_action(\'rest_api_init\', function() {
    register_rest_route(\'mysite/v1\', \'/ads/\', {
        [ 
            \'methods\'  => \'GET\',
            \'callback\' => \'get_ads\',
        ],
        [
            \'methods\'    => \'POST\',
            \'callback\'   => \'create_ad\',
        ],
    ]);
});

function create_ad() {
    global $wpdb;

    $result = $wpdb->query(...);

    return $result;
}

function get_ads() {
    global $wpdb;

    $ads = $wpdb->get_results(\'SELECT * from `ads`\');

    return $ads;
}

1 个回复
SO网友:Jürgen Fink

很晚了,但可能对其他读者有所帮助,因为我专门为这个问题的上述代码添加了解决方案。

解决方案:权限回调函数WordPress: 版本5.7.2PHP: 7.4版host: hostmonster。com
client: Windows 10browsers: 在Chrome、Firefox甚至Edge上进行了测试

Code (PHP 函数中的代码。安装主题的php):

add_action(\'rest_api_init\', function() {
    /**
     * Register here your custom routes for your CRUD functions
     */
    register_rest_route( \'mysite/v1\', \'/ads/\', array(
        array(
            \'methods\'  => WP_REST_Server::READABLE, // = \'GET\'
            \'callback\' => \'get_ads\',
            // Always allow, as an example
            \'permission_callback\' => \'__return_true\'
        ),
        array(
            \'methods\'  => WP_REST_Server::CREATABLE, // = \'POST\'
            \'callback\' => \'create_ad\',
            // 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() {
    // 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 ) );
    };
    return true;
};

function create_ad() {
    global $wpdb;

    $result = $wpdb->query(...);

    return $result;
}

function get_ads() {
    global $wpdb;

    $ads = $wpdb->get_results(\'SELECT * from `ads`\');

    return $ads;
}
确保在HTML页面中包含credentials: \'same-origin\' 在HTTP请求中。

Code (HTML 带内联<script> ... </script>):

<script>

// Here comes the REST API part:
// HTTP requests with fetch() promises

function getYourAds() {
  let url = \'https://example.com/wp-json/mysite/v1/ads/\';
  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 insertYourAds(data) {
  let url = \'https://example.com/wp-json/mysite/v1/ads/\';
  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] )) { ...Permission Callback 不仅检查是否在客户端浏览器上设置了Cookie,but it seems also to check its value (the JWT token).

因为我用我的初始代码进行了dobble检查,传递了一个错误的令牌,消除了cookie,或者让会话保持打开状态,但在后端更改了站点的密码(因此WordPress将创建一个新的令牌,因此set的值wp_postpass_ cookie将更改)并且所有测试都正确进行-REST API blocked, not only verifying presence of cookie, but also its value (这很好-谢谢WordPress团队)。

Sources:
我在FAQ section:

Why is the REST API not verifying the incoming Origin header? Does this expose my site to CSRF attacks?

由于WordPress REST API不验证传入请求的原始标头,因此可以从任何站点访问公共REST API端点。这是一个有意的设计决策。

然而,WordPress有一个现有的CSRF保护机制,该机制使用nonces。

根据我目前的测试,the WP-way of authentication works perfectly well.

为wordpress团队竖起大拇指REST API Handbook:

REST API Handbook / Extending the REST API / Routes and Endpoints
REST API Handbook / Extending the REST API / Adding Custom Endpoints

对于那些对我的发现的完整故事感兴趣的人,请点击链接到我的帖子,并附上答案、代码片段和其他发现。

How to force Authentication on REST API for Password protected page using custom table and fetch() without Plugin