Node.js实现HMAC认证接口时返回401 Unauthorised错误如何解决
HMAC API 401错误问题排查
核心错误点(直接导致401)
- 请求头字段名大小写错误:Node.js的
http.request配置项中,请求头的字段名是小写的headers,你的代码写成了大写的Headers,导致Authorization、Date等认证相关请求头都没有被正常发送,服务端拿不到认证信息直接返回401。 - 请求体前后不一致:生成表征字符串时传入的
body是原始的RequestContent,但实际发送请求时你额外做了JSON.stringify(RequestContent)处理,两者内容完全不同,对应的Base64编码不一致,服务端校验签名直接失败。 - Date头生成时机错误:
requestDt是在模块初始化时生成的固定值,若接口调用时间和模块加载时间差超过服务端允许的时间偏移(通常为1~5分钟),会被判定为非法请求返回401,应该每次请求时重新生成当前UTC时间。 - 缺失utf8模块引入:代码里直接调用
utf8.encode(body),但Node.js没有全局utf8对象,若未引入对应依赖会导致表征字符串生成异常,签名计算错误。 - URI协议不匹配:你代码里
_estimateBaseUrl用的是http,如果服务端实际走的是https,生成表征字符串时的完整URI就和服务端接收到的不一致,导致签名校验失败。
其他需要修复的逻辑问题
getEstimateID没有封装成Promise,你用await调用时无法正常拿到返回结果,请求成功后的响应也没有正确返回给前端。- 生成HMAC签名时没有显式指定
utf8编码,部分场景下可能出现编码不一致导致签名错误。
核心代码修正示例
const crypto = require("crypto"); const moment = require('moment'); const https = require('https'); // 服务端是https的话用https模块 let _apiKey = "你的实际API_KEY"; let _estimateBaseUrl = "https://estimationapi.zirmed.com/1.0"; // 修正为实际协议 let _estimateTransaction = "/estimate"; let CustomerID = '你的实际CustID'; let RequestContent = {}; // 你的实际请求数据对象 getEstimate = async (req, res) => { // 每次请求重新生成当前时间 const requestDt = new Date().toUTCString(); // 统一处理请求体,避免前后不一致 const postBody = JSON.stringify(RequestContent); try { let signature = await createRequestAuth(_estimateTransaction, "POST", requestDt, CustomerID, postBody); let response = await getEstimateID(signature, requestDt, postBody); res.send(response); } catch (err) { res.status(500).send(err.message); } }; createRequestAuth = (uriPath, method, requestDate, custId, body = "") => { let fullUri = new URL(uriPath, _estimateBaseUrl).toString().toLowerCase(); let authHashString = CreateAuthHashString(method, fullUri, requestDate, custId, body); // 显式指定utf8编码避免编码差异 let signature = crypto.createHmac('sha256', Buffer.from(_apiKey, 'utf8')) .update(authHashString, 'utf8') .digest("base64"); return signature; } getEstimateID = (signature, requestDt, postBody) => { return new Promise((resolve, reject) => { const data = Buffer.from(postBody, 'utf8'); const options = { hostname: 'estimationapi.zirmed.com', port: 443, // https对应443,http对应80 path: '/1.0/estimate', method: 'POST', // 修正为小写headers headers: { Authorization: `HMAC ${CustomerID}:${signature}`, Date: requestDt, "Content-Type": `application/json`, 'Content-Length': data.length } } const req = https.request(options, res => { let result = ''; res.on('data', d => { result += d }) res.on('end', () => resolve({statusCode: res.statusCode, data: result})) }) req.on('error', error => reject(error)) req.write(data) req.end() }) } CreateAuthHashString = (method, fullUri, requestDate, custId, body) => { // 直接用Buffer处理,不需要额外引入utf8模块 let content = Buffer.from(body, 'utf8').toString('base64'); return method + fullUri + moment(requestDate).format('yyyy-MM-DDTHH:mm:ss') + custId + content; } module.exports = { getEstimate }
内容的提问来源于stack exchange,提问作者Manoj Patidar
相关产品推荐
相关产品推荐

