如何正确认证Shopware 6的base-app-url请求?
我最近在基于Shopware 6 Admin SDK扩展后台功能时,踩了一个关于base-app-url请求认证的坑,折腾了好一阵才找到问题所在,分享给遇到同样问题的朋友:
问题背景
当我们借助Admin SDK扩展Shopware 6后台功能时,需要在应用的manifest文件里配置base-app-url作为入口点。所有请求都需要正确的认证,但坑点来了:base-app-url的GET请求没办法像普通模块的GET请求那样完成认证——我自己写的Node.js认证代码,验证模块请求完全正常,但一验证base-app-url的请求就抛出Signature could not be verified!错误。
问题细节
先给大家看一下相关的内容:
- 正常工作的模块请求示例(签名验证无问题):
GET /module/my-module?shop-id=SW12345×tamp=1699999999&signature=abc123...&privileges=[]
对应的PHP验证代码(官方风格,可正常验证):
public function validate(Request $request): bool { $query = $request->query->all(); $signature = $query['signature'] ?? ''; unset($query['signature']); $signedString = http_build_query($query, '', '&', PHP_QUERY_RFC3986); $expectedSignature = hash_hmac('sha256', $signedString, $this->secret); return hash_equals($expectedSignature, $signature); }
base-app-url请求示例(验证失败):
GET /my-app?shop-id=SW12345×tamp=1699999999&signature=def456...&privileges=%5B%22product%3Aread%22%5D
- 最初的Node.js认证代码(有问题):
function validateRequest(req, secret) { const { signature, ...query } = req.query; const signedString = new URLSearchParams(query).toString(); const expectedSignature = crypto.createHmac('sha256', secret) .update(signedString) .digest('hex'); return expectedSignature === signature; }
根因分析
问题出在Express的自动解码行为上:Express会自动把req.query里的参数进行URL解码,比如把%5B%22product%3Aread%22%5D解码成["product:read"]。但Shopware生成签名时,用的是编码后的原始query参数,如果我们用解码后的参数重新生成签名字符串,自然就和Shopware传来的签名对不上了,也就抛出了验证失败的错误。
解决方案
要解决这个问题,我们需要获取Shopware传来的原始未解码的query字符串,而不是用Express解码后的req.query。可以通过req.originalUrl来提取原始query部分,再处理:
修正后的Node.js代码:
const crypto = require('crypto'); function validateRequest(req, secret) { // 从原始URL中提取query部分(去掉路径和开头的?) const queryString = req.originalUrl.split('?')[1] || ''; // 把query字符串拆分成键值对,排除signature参数 const params = new URLSearchParams(queryString); const signature = params.get('signature'); params.delete('signature'); // 用原始的编码参数生成签名字符串 const signedString = params.toString(); const expectedSignature = crypto.createHmac('sha256', secret) .update(signedString) .digest('hex'); return expectedSignature === signature; }
这样就能确保我们用和Shopware生成签名时完全一致的参数字符串来做HMAC验证,自然就能通过认证了。
内容的提问来源于stack exchange,提问作者lucamario

