如何使用自定义NodeJS服务器在Shopify店铺安全展示额外客户数据
实现方案核心逻辑
你当前的客户面店铺页面场景不需要使用App Bridge(App Bridge仅用于Shopify后台嵌入式应用开发),直接用Shopify官方提供的App Proxy即可满足所有安全要求:
- App Proxy会作为中转层,所有前端请求先到Shopify再转发到你的NodeJS API,避免暴露你的API真实地址
- Shopify会自动在转发请求里注入已登录用户的ID,伪造无效
- 自带签名校验机制,可确保请求确实来自你指定的Shopify店铺
- 避免你原来直接在URL传邮箱的安全风险:明文传邮箱很容易被篡改遍历,泄露所有用户数据
分步实现教程
第一步:配置Shopify App Proxy
- 进入你的Shopify合作伙伴后台,找到对应应用,进入「应用代理」配置页
- 代理路径自定义即可,比如前缀选
/a,子路径填customer-data,后续店铺内访问/a/customer-data就会自动转发到你的NodeJS API - 代理URL填你的NodeJS服务的接口地址,比如
https://你的API域名/shopify/customer-data - 保存配置后,Shopify会生成专属的签名密钥,后续服务端校验要用到,存在你的服务端环境变量里即可。
第二步:Liquid模板注入安全参数
不要在请求里传明文邮箱,Shopify的Liquid自带全局变量,只有已登录用户才会生成customer变量,直接把官方的客户ID和店铺域名注入给Vue组件即可:
{% if customer %} <!-- 仅已登录用户才会渲染Vue组件,未登录用户访问/account页Shopify会自动跳转到登录页 --> <div id="vue-app" data-customer-id="{{ customer.id }}" data-shop-domain="{{ shop.permanent_domain }}"></div> {% endif %}
Vue组件内发送请求的时候,直接调用App Proxy地址,不要直接请求你的NodeJS API地址:
// Vue组件请求逻辑示例 const customerId = document.getElementById('vue-app').dataset.customerId const shopDomain = document.getElementById('vue-app').dataset.shopDomain // 请求走Shopify App Proxy中转,不直接暴露你的API地址 fetch(`/a/customer-data?customer_id=${customerId}&shop_domain=${shopDomain}`) .then(res => res.json()) .then(data => { // 处理返回的自定义客户数据 })
第三步:NodeJS(Express/Feathers)端三层安全校验
这一步是核心,所有校验通过后再返回用户数据:
1. 编写App Proxy签名校验中间件
首先校验请求确实是Shopify官方转发的,不是伪造的,Express示例如下,Feathers可以直接封装成钩子使用:
import crypto from 'crypto' const validateShopifyProxySignature = (req, res, next) => { const { signature, ...query } = req.query // 先校验时间戳,防止重放攻击,超过5分钟的请求直接拒绝 if (Date.now() / 1000 - Number(query.timestamp) > 300) { return res.status(401).send('请求超时') } // 按照Shopify官方规则生成校验哈希:把所有参数按键升序排序拼接,用App Proxy密钥做HMAC SHA256加密 const sortedQueryStr = Object.keys(query).sort().map(key => `${key}=${query[key]}`).join('') const computedHash = crypto.createHmac('sha256', process.env.SHOPIFY_APP_PROXY_SECRET).update(sortedQueryStr).digest('hex') // 哈希对比不一致直接拒绝 if (computedHash !== signature) { return res.status(403).send('非法请求') } next() }
2. 身份与权限校验
签名校验通过后,再验证用户身份和访问权限:
// 你的API接口逻辑 app.get('/shopify/customer-data', validateShopifyProxySignature, async (req, res) => { const { logged_in_customer_id, customer_id, shop_domain } = req.query // 校验1:logged_in_customer_id是Shopify官方注入的参数,存在才说明用户已登录 if (!logged_in_customer_id) { return res.status(401).send('请先登录店铺') } // 校验2:前端传的客户ID必须和Shopify注入的登录用户ID一致,防止用户篡改参数访问他人数据 if (logged_in_customer_id !== customer_id) { return res.status(403).send('无权访问他人数据') } // 校验3:仅允许你指定的店铺访问,防止其他店铺的请求打到你的API if (shop_domain !== process.env.ALLOWED_SHOP_DOMAIN) { return res.status(403).send('不允许的店铺访问') } // 所有校验通过后,拿客户ID去你的数据库查询对应绑定的邮箱,再返回关联的自定义数据即可 const userData = await 你的数据库查询方法({ shopify_customer_id: customer_id }) if (!userData) { return res.status(404).send('未查询到对应数据') } res.json(userData) })
第四步:额外安全加固
- 你的NodeJS API不要开启跨域配置,所有请求必须走App Proxy中转,避免外部直接调用
- 给接口加频率限制,防止被暴力爬取
- 永远不要在前端请求中传输邮箱、手机号等敏感信息,所有身份校验依赖Shopify官方注入的可信参数
内容的提问来源于stack exchange,提问作者Laurensius Adi
相关产品推荐
相关产品推荐

