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
相关产品推荐
相关产品推荐

