Azure Service Bus访问令牌C#实现报格式错误求助
Azure Service Bus SAS令牌格式错误问题排查
问题描述
在Postman中使用JavaScript代码可正常生成Azure Service Bus的SharedAccessSignature访问令牌,但迁移至C#实现后,生成的令牌始终提示access token is malformed,即使令牌格式外观相似,仍怀疑编码环节存在遗漏。
可正常工作的JavaScript代码
const sharedKeyName = "RootManageSharedAccessKey" const sharedKey = "xxx" const uri = "https://d365-service-bus-dev.servicebus.windows.net/XX-XX/subscriptions/XXX/messages/head" function createSharedAccessToken(uri, saName, saKey) { if (!uri || !saName || !saKey) { throw "Missing required parameter"; } var encoded = encodeURIComponent(uri); var now = new Date(); var week = 60*60*24*7; var ttl = Math.round(now.getTime() / 1000) + week; var signature = encoded + '\n' + ttl; const hash = CryptoJS.HmacSHA256(signature, saKey).toString(CryptoJS.enc.Base64) return 'SharedAccessSignature sr=' + encoded + '&sig=' + encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName; } // Set broker properties e.g. sessionId const brokerProperties = { 'SessionId':'123' } // Set broker proerties variable console.log() pm.variables.set("broker_properties", JSON.stringify(brokerProperties)); // Set access token variable pm.variables.set('access_token', createSharedAccessToken(uri, sharedKeyName, sharedKey));
存在问题的C#代码
if (string.IsNullOrEmpty(uri) || string.IsNullOrEmpty(saName) || string.IsNullOrEmpty(saKey)) { throw new ArgumentException("Missing required parameter"); } string encoded = Uri.EscapeDataString(uri); DateTime now = DateTime.UtcNow; int week = 60 * 60 * 24 * 7; long ttl = (long)(now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds + week; string signature = encoded + "\n" + ttl; byte[] keyBytes = Convert.FromBase64String(saKey); using (var hmac = new HMACSHA256(keyBytes)) { byte[] signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(signature)); string hash = Convert.ToBase64String(signatureBytes); Token ="SharedAccessSignature sr="+encoded+"&sig="+Uri.EscapeDataString(hash)+"&se="+ttl+"&skn="+saName; } Console.WriteLine(Token);
问题原因及修正方案
1. 参数分隔符错误
C#代码中使用了HTML实体&作为参数分隔符,但Azure Service Bus的SAS令牌要求使用普通的&分隔参数。JS代码中虽写了&,但Postman在处理变量时会自动转换为&,而C#直接生成&会导致服务端无法正确解析令牌结构,触发格式错误提示。
2. URI编码规则不一致
JavaScript的encodeURIComponent与C#的Uri.EscapeDataString对部分字符的编码逻辑不同:
- JS的
encodeURIComponent不会编码!、'、(、)、*这些字符 - C#的
Uri.EscapeDataString会编码这些字符
这会导致签名生成时的原始字符串不一致,最终生成的哈希值不匹配,令牌无效。
3. 时间戳计算差异
JS中使用Math.round对时间戳进行四舍五入,而C#中直接截断取整,可能导致过期时间差1秒(虽一般不会触发格式错误,但建议保持逻辑一致)。
修正后的C#代码
if (string.IsNullOrEmpty(uri) || string.IsNullOrEmpty(saName) || string.IsNullOrEmpty(saKey)) { throw new ArgumentException("Missing required parameter"); } // 实现与JS encodeURIComponent等效的编码逻辑 string encoded = EncodeUriComponent(uri); DateTime now = DateTime.UtcNow; int week = 60 * 60 * 24 * 7; // 时间戳四舍五入,与JS逻辑一致 long ttl = (long)Math.Round((now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds) + week; string signature = encoded + "\n" + ttl; byte[] keyBytes = Convert.FromBase64String(saKey); using (var hmac = new HMACSHA256(keyBytes)) { byte[] signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(signature)); string hash = Convert.ToBase64String(signatureBytes); // 使用&作为参数分隔符,而非HTML实体 Token = $"SharedAccessSignature sr={encoded}&sig={Uri.EscapeDataString(hash)}&se={ttl}&skn={saName}"; } Console.WriteLine(Token); // 等效于JS encodeURIComponent的方法 private static string EncodeUriComponent(string value) { var result = new StringBuilder(); foreach (char c in value) { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') { result.Append(c); } else { result.Append('%' + string.Format("{0:X2}", (int)c)); } } return result.ToString(); }
内容的提问来源于stack exchange,提问作者hazim saleh
相关产品推荐
相关产品推荐

