WooCommerce Webhook签名验证失败:Node.js后端集成问题排查
解决WooCommerce Webhook签名验证不匹配的问题
我来帮你搞定这个签名验证失败的问题——核心原因其实出在你对请求体的处理方式上!
问题根源
你用了bodyParser.json()中间件,它会把HTTP请求的原始JSON字符串解析成JavaScript对象。但WooCommerce生成Webhook签名时,是基于原始的请求体字符串(也就是未被解析的、直接从HTTP请求里拿到的纯文本)来计算HMAC-SHA256哈希的。
当你用req.body(解析后的对象)传给CryptoJS时,CryptoJS会把这个对象重新序列化成字符串,但这个序列化后的格式和WooCommerce发送的原始字符串大概率不一样(比如键的顺序、空格、引号的处理细节),最终导致生成的哈希和签名不匹配。
修复方案
我们需要先获取原始的请求体字符串,用它来计算哈希,验证通过后再把原始字符串解析成JSON对象处理业务逻辑。具体步骤如下:
1. 安装依赖(如果用raw-body的话)
首先安装raw-body模块,它能帮我们方便地获取原始请求体:
npm install raw-body
2. 修改路由代码
替换掉原来的body-parser相关代码,改用原始请求体来计算哈希:
const rawBody = require('raw-body'); const CryptoJS = require('crypto-js'); router.post('/', async function (req, res) { try { const secret = 'ciPV6gjCbu&efdgbhfgj&¤"#&¤GDA'; const signature = req.header("x-wc-webhook-signature"); // 获取原始的请求体字符串 const rawRequest = await rawBody(req, { encoding: 'utf8' }); // 基于原始字符串计算HMAC-SHA256并转Base64 const hash = CryptoJS.HmacSHA256(rawRequest, secret).toString(CryptoJS.enc.Base64); if(hash === signature){ // 验证通过后,再解析原始字符串为JSON对象处理业务 const requestBody = JSON.parse(rawRequest); console.log('Webhook验证成功,请求内容:', requestBody); res.send('match'); } else { console.log('哈希不匹配', { 计算出的哈希: hash, 收到的签名: signature }); res.send("no match"); } } catch (err) { console.error('处理Webhook请求出错:', err); res.status(400).send('Bad Request'); } });
可选:用Node.js内置的crypto模块(更高效)
如果你不想用CryptoJS,也可以用Node.js自带的crypto模块来计算哈希,代码如下:
const crypto = require('crypto'); const rawBody = require('raw-body'); router.post('/', async function (req, res) { try { const secret = 'ciPV6gjCbu&efdgbhfgj&¤"#&¤GDA'; const signature = req.header("x-wc-webhook-signature"); const rawRequest = await rawBody(req, { encoding: 'utf8' }); // 用内置crypto生成HMAC const hmac = crypto.createHmac('sha256', secret); hmac.update(rawRequest); const hash = hmac.digest('base64'); if(hash === signature){ const requestBody = JSON.parse(rawRequest); res.send('match'); } else { res.send("no match"); } } catch (err) { res.status(400).send('Bad Request'); } });
额外注意事项
- 确保你Node.js代码里的
secret和WooCommerce后台配置的完全一致,包括所有特殊字符(比如你Secret里的¤,别复制错了) - 不要在这个路由之前使用任何会消耗请求体的中间件(比如
body-parser.json()),否则raw-body就拿不到原始数据了 - 检查WooCommerce Webhook的Content-Type设置,默认是
application/json,确保和你的请求体编码匹配
内容的提问来源于stack exchange,提问作者Unicco
相关产品推荐
相关产品推荐

