You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在register_rest_route中结合JWT正确使用permission_callback的问题

WordPress插件REST API权限回调审核不通过的问题分析与修复

问题根源

你的插件被WordPress.org拒绝,核心是以下不符合审核规范的问题:

1. 敏感接口设置了完全公开的权限回调

change-password是修改用户密码的敏感接口,但你的public_permission函数直接返回true,意味着任何未认证的请求都能调用该接口修改任意用户密码,这是严重的安全漏洞。

2. 自定义权限回调未遵循WP REST API规范

  • 直接使用getallheaders()获取请求头,该函数在部分服务器环境下兼容性差,WP REST API要求通过权限回调的$request参数(WP_REST_Request对象)获取请求信息。
  • JWT校验通过后仅设置了当前用户,但未验证该用户是否具备执行对应操作的权限(比如删除购物车优惠券需要用户拥有操作自身购物车的权限),不符合WP权限系统的设计要求。
  • 权限校验失败时仅返回false,没有返回标准的WP_Error对象,无法向客户端返回清晰的错误信息。

3. 冗余的自定义公开权限函数

WP已经内置了__return_true函数用于完全公开的接口,无需自定义public_permission函数,使用内置函数更符合官方规范。


修复方案

1. 修复修改密码接口的权限校验

修改密码接口绝对不能完全公开,必须限制为已登录用户修改自身密码:

// 修改密码路由(仅允许已登录用户修改自己的密码)
register_rest_route(MYPLUG_API_V1, 'change-password', [
    'methods'             => 'POST',
    'callback'            => [new MyPlug_Api_Auth(), 'changePassword'],
    'permission_callback' => function(WP_REST_Request $request) {
        // 检查用户是否登录
        if (!is_user_logged_in()) {
            return new WP_Error(
                'rest_not_logged_in',
                __('用户未登录', 'myplug'),
                ['status' => 401]
            );
        }

        // 确保只能修改自己的密码
        $current_user_id = get_current_user_id();
        $target_user_id = isset($request['user_id']) ? (int)$request['user_id'] : $current_user_id;
        
        if ($target_user_id !== $current_user_id) {
            return new WP_Error(
                'rest_forbidden',
                __('无权限修改该用户密码', 'myplug'),
                ['status' => 403]
            );
        }

        return true;
    },
]);

2. 重构受保护路由的权限校验函数

修改myplug_check_auth函数,遵循WP REST API规范,增加权限验证并返回标准错误:

function myplug_check_auth(WP_REST_Request $request): bool|WP_Error
{
    // 通过WP_REST_Request对象获取Authorization头
    $auth_header = $request->get_header('Authorization');

    if (empty($auth_header)) {
        return new WP_Error(
            'rest_no_auth_header',
            __('缺少Authorization请求头', 'myplug'),
            ['status' => 401]
        );
    }

    if (!preg_match('/Bearer\s(\S+)/', $auth_header, $matches)) {
        return new WP_Error(
            'rest_invalid_auth_format',
            __('Authorization头格式错误,应为Bearer <token>', 'myplug'),
            ['status' => 401]
        );
    }

    $token = $matches[1];
    $secret_key = defined('MYPLUG_SECRET_KEY') ? MYPLUG_SECRET_KEY : false;

    if (!$secret_key) {
        return new WP_Error(
            'rest_missing_secret',
            __('缺少JWT密钥配置', 'myplug'),
            ['status' => 500]
        );
    }

    $alg = 'HS256';
    try {
        $decoded = JWT::decode($token, new Key($secret_key, $alg));

        // 检查令牌是否过期
        if ($decoded->exp < time()) {
            return new WP_Error(
                'rest_token_expired',
                __('令牌已过期', 'myplug'),
                ['status' => 401]
            );
        }

        $user_id = $decoded->data->user->id;
        // 验证用户是否存在
        if (!user_exists($user_id)) {
            return new WP_Error(
                'rest_invalid_user',
                __('用户不存在', 'myplug'),
                ['status' => 401]
            );
        }

        wp_set_current_user($user_id);
        
        // 验证用户是否具备操作购物车优惠券的权限
        // 示例:如果是操作自身购物车,可校验购物车所属用户ID与当前用户ID一致
        // 这里假设使用自定义权限capability:manage_cart_coupons
        if (!current_user_can('manage_cart_coupons')) {
            return new WP_Error(
                'rest_forbidden',
                __('无权限操作购物车优惠券', 'myplug'),
                ['status' => 403]
            );
        }

        return true;
    } catch (Exception $e) {
        return new WP_Error(
            'rest_invalid_token',
            __('无效令牌:' . $e->getMessage(), 'myplug'),
            ['status' => 401]
        );
    }
}

注册受保护路由时无需修改,回调会自动接收$request参数:

// 受保护路由 
register_rest_route(MYPLUG_API_V1, '/cart/coupons', [
    'methods'             => 'GET',
    'callback'            => [new MyPlug_Api_Controller(), 'deleteAllCartCoupons'],
    'permission_callback' => 'myplug_check_auth',
]);

3. 规范完全公开接口的权限回调

如果你的插件有真正无需认证的公开接口(比如获取公开商品列表),直接使用WP内置的__return_true:

register_rest_route(MYPLUG_API_V1, 'public/products', [
    'methods'             => 'GET',
    'callback'            => [new MyPlug_Api_Public(), 'getProducts'],
    'permission_callback' => '__return_true',
]);

审核关键要点

  • 所有接口的权限回调必须与功能匹配,敏感操作绝对不能完全公开
  • 权限校验失败时必须返回WP_Error对象,包含明确的错误信息和正确的HTTP状态码
  • 优先使用WP内置的权限函数(is_user_logged_in()、current_user_can()等),自定义校验需兼容WP权限系统
  • 处理请求时依赖WP_REST_Request对象,避免使用服务器环境相关的全局函数

内容的提问来源于stack exchange,提问作者Moniruzzaman Saikat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:28:11