在 javascript 中实现 fetch api 请求数据预验签,核心做法是在发起请求前拦截并修改 request 对象或构建的 body。具体步骤是:获取原始请求参数,使用 hmac-sha256 等算法对关键参数(如时间戳、随机数、业务数据)进行签名计算,将生成的 sign 字段拼接到请求体或查询参数中,最后通过 fetch 发送包含签名的新请求。这样做能确保服务端接收到的数据未被篡改,且请求来源合法。

如何封装一个通用的带签名功能的 fetch 函数
要实现通用的预验签,可以创建一个包装函数,它接受原始配置和密钥,自动完成签名注入。关键在于处理 GET 和 POST 不同请求体的拼接逻辑,并确保时间戳和随机数的唯一性以防止重放攻击。

const crypto = require('crypto');
function createSignedFetch(secretKey) {
return async function(url, options = {}) {
const timestamp = Date.now();
const nonce = Math.random().toString(36).substring(2);
let bodyData = options.body;
if (typeof bodyData === 'object') {
bodyData = JSON.stringify(bodyData);
}
// 构造签名字符串: timestamp + nonce + body
const signStr = `${timestamp}${nonce}${bodyData}`;
const sign = crypto.createHmac('sha256', secretKey)
.update(signStr)
.digest('hex');
// 更新请求头或 body
const headers = { ...options.headers, 'X-Timestamp': timestamp, 'X-Nonce': nonce, 'X-Sign': sign };
// 如果是 POST/PUT,可能需要将签名放入 body
if (options.method && ['POST', 'PUT'].includes(options.method.toUpperCase())) {
const parsedBody = typeof bodyData === 'string' ? JSON.parse(bodyData) : bodyData;
parsedBody.sign = sign;
parsedBody.timestamp = timestamp;
parsedBody.nonce = nonce;
options.body = JSON.stringify(parsedBody);
}
return fetch(url, { ...options, headers });
};
}
// 使用示例
const signedFetch = createSignedFetch('my-secret-key');
signedFetch('/api/data', {
method: 'POST',
body: { name: 'test', value: 123 }
}).then(res => res.json()).then(console.log);
上述代码定义了一个高阶函数 createSignedFetch,它返回一个新的 fetch 代理。调用时,它会生成 timestamp、nonce 和 sign。对于 POST 请求,它将签名信息直接注入到 JSON Body 中;对于 GET 请求,通常建议将签名作为 Query Params 传递,此处简化为 Header 传递以展示核心逻辑。运行该代码后,服务端收到请求即可验证 X-Sign 是否匹配。
如何处理 GET 请求的参数签名与 URL 拼接
GET 请求没有 Body,因此签名必须附加到 URL 查询参数中。这需要解析原始 URL,提取现有参数,合并签名相关参数(sign, timestamp, nonce),并重新序列化 URL。同时,签名字符串的构造顺序必须与服务端严格一致,通常遵循“参数名 ASCII 升序排列”或“固定字段拼接”规则。

function getSignedUrl(baseUrl, params, secretKey) {
const urlObj = new URL(baseUrl);
const timestamp = Date.now();
const nonce = Math.random().toString(36).substring(2);
// 1. 收集所有待签名参数
const allParams = { ...params, timestamp, nonce };
// 2. 按 Key 排序并拼接成字符串 key1=value1&key2=value2
const sortedKeys = Object.keys(allParams).sort();
const signStr = sortedKeys.map(key => `${key}=${allParams[key]}`).join('&');
// 3. 计算签名
const sign = crypto.createHmac('sha256', secretKey)
.update(signStr)
.digest('hex');
// 4. 将签名加入 URL
urlObj.searchParams.set('sign', sign);
urlObj.searchParams.set('timestamp', timestamp);
urlObj.searchParams.set('nonce', nonce);
return urlObj.toString();
}
// 使用示例
const finalUrl = getSignedUrl('https://api.example.com/user', { id: 100 }, 'secret');
console.log(finalUrl);
fetch(finalUrl).then(...)
此段代码展示了 GET 请求的处理流程。首先实例化 URL 对象以便操作查询参数。接着,将所有参数(包括业务参数和时间戳等)放入一个对象,按键名排序后拼接成标准签名字符串。计算出的 sign 以及 timestamp、nonce 最终通过 searchParams.set 添加到 URL 中。这种处理方式保证了 URL 的可读性和签名的完整性。执行后,输出的 URL 将包含所有必要的验证字段。
当签名验证失败时前端应如何捕获和处理异常
当服务端返回 401 或 403 状态码,或响应体中包含错误标志时,表示签名验证失败。前端应在 fetch 的 then 链或 async/await 块中检查 response.ok 或特定错误字段。常见的处理策略包括:记录日志、提示用户重试、或者触发 Token/Key 刷新机制(如果签名依赖于动态密钥)。此外,需区分网络错误和服务端业务错误。
SIGN_INVALID
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











