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

Woocommerce Webhook对接Yii2 API返回401错误求助

解决Woocommerce Webhook推送到Yii2的401验证问题

问题根源

你当前用的HttpBearerAuth是针对Bearer Token的认证逻辑,但Woocommerce Webhook的身份验证是基于请求体的HMAC签名,不是Bearer Token,所以不管怎么传access_token都会返回401。

具体修复步骤

1. 替换认证方式,改用Woocommerce Webhook签名验证

移除原有的HttpBearerAuth,添加自定义过滤器来验证Woocommerce发送的X-WC-Webhook-Signature请求头。

2. 修改控制器代码

以下是调整后的完整代码,核心替换了认证逻辑,并新增了专门的Webhook接收动作:

<?php
namespace app\modules\api\controllers;
use Yii;
use yii\rest\ActiveController;
use yii\filters\auth\AuthMethod;
use app\modules\api\resources\Cors;
use app\modules\api\resources\ProductsResource;

// 自定义Woocommerce Webhook认证过滤器
class WoocommerceWebhookAuth extends AuthMethod
{
    public $webhookSecret; // 对应Woocommerce Webhook设置里的Secret

    public function authenticate($user, $request, $response)
    {
        $signatureHeader = $request->getHeaders()->get('X-WC-Webhook-Signature');
        if (!$signatureHeader) {
            $this->handleFailure($response);
            return null;
        }

        // 读取原始请求体(不能用Yii的post方法,会丢失原始格式)
        $rawBody = file_get_contents('php://input');
        $expectedSignature = base64_encode(hash_hmac('sha256', $rawBody, $this->webhookSecret, true));

        // 安全对比签名,防止时序攻击
        if (!hash_equals($expectedSignature, $signatureHeader)) {
            $this->handleFailure($response);
            return null;
        }

        // 验证通过,返回虚拟身份(Webhook无需用户登录,仅验证签名)
        return $user->loginByAccessToken('webhook-valid', get_class($this));
    }
}

class ProductsController extends ActiveController
{
    public $modelClass = ProductsResource::class;
    public $enableCsrfValidation = false;
    public static function allowedDomains()
    {
        return ['*']; // 上线后建议限制为Woocommerce服务器IP
    }

    public function behaviors()
    {
        $behaviors = parent::behaviors();

        // 替换原有认证逻辑为自定义的Woocommerce签名验证
        unset($behaviors['authenticator']);
        $behaviors['authenticator'] = [
            'class' => WoocommerceWebhookAuth::class,
            'webhookSecret' => '你的Woocommerce Webhook Secret', // 替换成后台设置的Secret
            'except' => ['options'],
        ];

        // 保留并调整CORS配置,确保允许签名头
        return array_merge($behaviors, [
            'corsFilter' => [
                'class' => Cors::className(),
                'cors' => [
                    'Origin' => static::allowedDomains(),
                    'Access-Control-Request-Method' => ['GET','HEAD','POST','PUT','PATCH', 'OPTIONS'],
                    'Access-Control-Allow-Credentials' => false,
                    'Access-Control-Max-Age' => 3600,
                    'Access-Control-Allow-Headers' => ['origin', 'authorization', 'X-CSRF-TOKEN','X-Requested-With','X-Auth-Token','content-type', 'X-WC-Webhook-Signature'],
                    'Access-Control-Request-Headers' => ['*'],
                    'Access-Control-Expose-Headers' => []
                ],
            ],
            'verbs' => [
                'class' => \yii\filters\VerbFilter::className(),
                'actions' => [
                    'webhook' => ['POST'], // 仅允许POST请求Webhook
                ],
            ],
        ]);
    }

    protected function verbs()
    {
        return [
            'webhook' => ['POST'],
            'view' => ['GET', 'HEAD'],
            'create' => ['POST'],
            'update' => ['PUT', 'PATCH'],
            'delete' => ['DELETE'],
        ];
    }

    public function actions()
    {
        $actions = parent::actions();
        unset($actions['create'], $actions['update'], $actions['index'], $actions['view']);
        return $actions;
    }

    // 专门用于接收Woocommerce Webhook的动作
    public function actionWebhook()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
        // 读取原始请求体
        $rawData = file_get_contents('php://input');
        $webhookData = json_decode($rawData, true);

        // 先打印日志确认数据接收状态
        Yii::info('Received Woocommerce Webhook: ' . $rawData, 'woocommerce-webhook');

        // 后续在这里添加库存更新逻辑
        return ['status' => 'success', 'message' => 'Webhook received'];
    }
}

3. Woocommerce后台Webhook配置

在Woocommerce后台设置 > 高级 > Webhooks新建Webhook:

  • 主题选择订单已完成或库存更新(根据你的需求)
  • 交付URL填Yii2的Webhook地址,比如https://你的域名/api/products/webhook
  • 秘密(Secret)填控制器里设置的webhookSecret,两边必须完全一致
  • 交付方法选POST,格式选JSON

4. 测试验证

用Postman模拟请求时:

  • 请求方法选POST
  • 添加请求头X-WC-Webhook-Signature,值为用你的Secret和请求体生成的HMAC SHA256签名(base64编码)
  • 请求体填入Woocommerce Webhook的模拟JSON数据

额外提示

  • 上线后把allowedDomains里的*改成Woocommerce服务器的IP,提升安全性
  • 建议把Webhook Secret放到Yii2的params.php配置文件中,不要硬编码在控制器里
  • 如果仍有问题,检查服务器是否拦截了php://input的读取,或防火墙/CDN是否修改了请求头

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:43:10