Azure Logic App调用ACS REST API发送SMS遇HTTP 401未授权错误求助
你的401未授权错误,核心原因是HMAC签名生成的细节不符合ACS的要求,以下是逐一排查和修复步骤:
1. Authorization头缺失必填参数
ACS的HMAC-SHA256授权头必须包含SharedAccessKeyName,对应你ACS资源的访问策略名称(比如默认的root,或者你自定义的策略名)。你当前的代码完全没加这个参数,导致ACS无法识别签名对应的权限策略,直接拒绝请求。
修复代码:
修改授权头生成逻辑,添加SharedAccessKeyName:
// 替换为你ACS资源的访问策略名称,比如默认的"root" const sharedAccessKeyName = "root"; const authorizationHeader = `HMAC-SHA256 SignedHeaders=date;host;x-ms-content-sha256&SharedAccessKeyName=${sharedAccessKeyName}&Signature=${signature}`;
2. 请求体哈希不一致
你在Function里用JSON.stringify(requestBody)生成请求体的SHA256哈希,但Logic App发给ACS的请求体必须和这个序列化结果完全一模一样(包括空格、键的顺序、引号格式)。如果Logic App的Compose输出或后续传递中自动调整了JSON格式,哈希就会不匹配,签名直接失效。
验证与修复:
- 在Function的返回体里加个
requestBodyJson: JSON.stringify(requestBody),同时在Logic App里抓一下调用ACS前的请求体,对比两者是否完全相同。 - 如果不一致,要么在Logic App里用
string()函数把Compose输出转成字符串直接传,要么让Function直接返回序列化后的请求体字符串,供Logic App调用ACS时用。
3. Date头必须严格同步
ACS要求签名里的date值和请求的Date头完全一致,而且请求时间和服务器时间差不能超过15分钟。如果Logic App拿到Function返回的Date头后延迟太久才调用ACS,或者中途改了Date值,签名就会失效。
验证与修复:
- Logic App调用ACS时,直接用Function返回的
Date头值,别做任何修改。 - 检查Function生成的
dateStr格式是不是标准UTC格式(比如Wed, 10 Jul 2024 12:34:56 GMT),别搞出格式错误。
4. URL路径签名范围确认
ACS签名规则里,stringToSign的URL部分只需要绝对路径(不含主机和查询参数),你当前代码里的url = "/sms"是对的(api-version属于查询参数,不用包含在签名里),这部分不用改,但要确保Logic App调用ACS的URL确实是/sms,查询参数api-version=2021-03-07没错。
5. Host头正确性
确认Logic App调用ACS时的Host头和Function里的hostStr完全一致(就是acs-myendpointname.europe.communication.azure.com,别带:443端口)。如果Logic App自动加了端口,手动把Host头改成正确的值。
完整修复后的Function代码
const CryptoJS = require('crypto-js'); module.exports = async function (context, req) { const dateStr = new Date().toUTCString(); let requestBody = req.body; // 序列化请求体,确保和Logic App发给ACS的完全一致 const requestBodyJson = JSON.stringify(requestBody); const hashedBodyStr = CryptoJS.SHA256(requestBodyJson).toString(CryptoJS.enc.Base64); const endpoint = 'https://acs-myendpointname.europe.communication.azure.com'; const hostStr = endpoint.replace('https://', ''); const url = `/sms`; const stringToSign = `${req.method}\n${url}\n${dateStr};${hostStr};${hashedBodyStr}`; // 替换为你的ACS访问密钥 const key = CryptoJS.enc.Base64.parse('6LdYArmyververysecretaccesskeyAAAAAZCSUpsh'); // 替换为你的ACS访问策略名称 const sharedAccessKeyName = "root"; const signature = CryptoJS.HmacSHA256(stringToSign, key).toString(CryptoJS.enc.Base64); const authorizationHeader = `HMAC-SHA256 SignedHeaders=date;host;x-ms-content-sha256&SharedAccessKeyName=${sharedAccessKeyName}&Signature=${signature}`; context.res = { headers: { 'Date': dateStr, 'x-ms-content-sha256': hashedBodyStr, 'Authorization': authorizationHeader }, body: { message: 'Headers set successfully', signedString: stringToSign, signature: signature, requestBodyJson: requestBodyJson // 用于验证请求体一致性 } }; };
额外验证步骤
- Logic App里直接用Function返回的
requestBodyJson作为请求体发给ACS,避免格式差异。 - 把Postman里的请求体、Date、Host等参数代入Function逻辑,看生成的签名和Postman的是否一致,验证代码逻辑是否正确。
内容的提问来源于stack exchange,提问作者joostvdlinden

