You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Widget请求的Rest API身份认证机制实现咨询

嵌入第三方站点的Widget请求防伪造认证方案(NodeJS适配)

这套方案的核心逻辑是把签名生成能力收敛到后端侧,前端永远不会接触到核心密钥,抓包获取的临时凭证有效期极短且和请求上下文强绑定,几乎没有批量复用仿造的价值。

前置配置

  • 给每个接入的第三方站点分配唯一的app_key和app_secret:app_key属于公开信息,可以直接放到Widget的前端初始化参数里;app_secret为核心密钥,仅告知接入方存储在他们自己的后端服务中,绝对不能暴露到前端代码。

完整校验流程

1. 前端Widget签名获取逻辑

Widget初始化时,不要直接请求你的NodeJS服务接口,先请求接入方自己的后端接口,由接入方后端按你指定的规则生成临时签名:

  • 签名规则参考:sign = HMAC-SHA256(app_secret + 10分钟级时间戳 + 接入方站点域名 + 所有请求参数按key排序拼接的字符串)
  • 接入方后端将app_key、timestamp、sign三个参数返回给前端Widget

2. 请求携带规则

Widget每次向你的NodeJS后端发起请求时,默认携带上述三个参数,浏览器会自动带上Origin/Referer请求头,不需要额外处理。

3. NodeJS后端校验逻辑

按优先级依次做以下校验,任意一步不通过直接拒绝请求:

  • 校验请求携带的app_key是否在你的合法接入清单中
  • 校验Origin/Referer头的域名是否和该app_key绑定的接入域名一致,无Origin头的请求走降级校验逻辑,单独统计频次
  • 校验时间戳有效期:10分钟级时间戳允许前后各1个时间窗口,即签名最多20分钟内有效,避免抓包后长期复用
  • 用该app_key对应的app_secret,按照完全相同的规则重新计算签名,和请求携带的sign做比对,一致才放行

额外加固规则

  • 所有请求参数参与签名计算:即使签名被抓包,也只能复用对应请求的固定参数,修改任意请求参数都会导致签名校验失败
  • 做app_key+IP维度的限流,即使签名被破解,也无法发起大规模恶意请求
  • 敏感操作接口额外增加人机校验,拦截批量自动化伪造请求

隐私插件拦截Origin/Referer头的兼容处理:允许这类请求通过签名校验,但单独统计该app_key下无Origin请求的占比,超过阈值触发人工审核,避免被滥用。

示例代码(NodeJS校验逻辑)

const crypto = require('crypto');
// 实际生产环境请存在数据库/配置中心,不要硬编码
const legalAppMap = {
  'test_abc123': {
    appSecret: 'test_secret_456',
    allowDomains: ['https://customer-a.com', 'https://test.customer-a.com']
  }
}

function validateWidgetRequest(req) {
  const { app_key, timestamp, sign } = req.headers; // 也可以从query/body取
  const origin = req.headers.origin || '';
  // 校验app_key合法性
  if (!legalAppMap[app_key]) return false;
  const currentApp = legalAppMap[app_key];
  // 校验域名合法性
  if (origin && !currentApp.allowDomains.includes(origin)) return false;
  // 校验时间有效性(10分钟级时间戳,允许前后20分钟误差)
  const currentTimeSlot = Math.floor(Date.now() / 1000 / 60 / 10);
  const reqTimeSlot = Math.floor(Number(timestamp) / 60 / 10);
  if (Math.abs(currentTimeSlot - reqTimeSlot) > 1) return false;
  // 所有参数按key排序拼接(示例只取query参数,body参数同理处理)
  const sortedQueryStr = Object.keys(req.query).sort().map(k => `${k}=${req.query[k]}`).join('&');
  // 计算签名对比
  const calculatedSign = crypto.createHmac('sha256', currentApp.appSecret)
    .update(`${currentApp.appSecret}${timestamp}${origin}${sortedQueryStr}`)
    .digest('hex');
  return calculatedSign === sign;
}

内容的提问来源于stack exchange,提问作者Cycl0n3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 16:36:02