Express配置JWT白名单后Stripe webhook请求仍提示未授权
问题根因
Stripe的请求没有命中你配置的JWT白名单,被express-jwt中间件直接拦截返回了未授权错误。
Postman直连能通过校验,是因为直连请求的路径完全符合你写的匹配规则,但Stripe发过来的实际请求存在你没覆盖到的隐性偏差,常见偏差点有三个:
- 路径格式差异:Stripe后台配置的webhook地址可能带尾斜杠、查询参数,经过反向代理(Nginx、API网关)时还可能被重写拼接额外字符,和你Postman测试的路径不完全一致
- 依赖库匹配bug:你用的
express-unless(express-jwt的unless功能依赖这个库)在混合使用字符串路径、正则、methods限制的配置格式时,部分版本存在正则规则优先级失效的问题,会出现同路径不同来源请求匹配结果不一致的情况 - 解析顺序冲突:如果把全局JSON解析中间件放在JWT和webhook路由前面,不仅会导致后续Stripe签名校验失败,部分场景下还会干扰中间件的路径匹配逻辑
修复步骤
- 替换现有模糊的正则匹配规则,改用自定义函数做白名单判断,从根源避免匹配偏差。临时加一行日志打印Stripe请求的真实路径,方便排查:
const UNSECURED_ROUTES = { custom: (req) => { // 临时日志,上线可删除,用于确认Stripe请求的真实路径和方法 console.log('Incoming request:', req.method, req.originalUrl); // 放行所有webhook路径,忽略尾斜杠、查询参数差异 if (req.path.startsWith('/api/v1/webhook')) { return true; } // 保留原有其他白名单规则 if (req.path === '/api/v1/' && req.method === 'GET') return true; if (req.path.startsWith('/api/v1/callback') && ['GET', 'POST'].includes(req.method)) return true; return false; } };
- 调整中间件挂载顺序,把Stripe webhook需要的原始body解析放在最前面,避免签名校验和路径匹配冲突:
Stripe的签名校验要求拿到未被解析的原始请求Buffer,不能用全局JSON解析处理后的body,所以必须单独给webhook路由配置raw解析,并且放在JWT中间件之前:
// 1. 先处理webhook路由的原始body解析 app.use('/api/v1/webhook', express.raw({ type: 'application/json' })); // 2. 再挂载JWT校验中间件 app.use(jwtCheck.unless(UNSECURED_ROUTES)); // 3. 最后挂载全局JSON解析和业务路由 app.use(express.json()); app.use('/api/v1', apiRoutesv1);
- 检查Stripe后台配置的webhook地址,确保和Postman测试的地址完全一致,没有多余的尾斜杠、拼写错误,确认线上服务部署的是最新代码,没有旧版本进程缓存。
验证方式
配置生效后重新触发Stripe的webhook测试,查看日志确认请求路径打印正确,白名单命中后就不会再出现credentials_required的报错,请求会正常进入webhook处理逻辑。之前Postman测试出现的签名不匹配问题,也会因为调整了body解析顺序自动修复。
内容的提问来源于stack exchange,提问作者Griff
相关产品推荐
相关产品推荐

