Azure Notification Hub Java调用报无效授权令牌签名错误求助
看起来你的问题核心出在SAS令牌的生成逻辑上,和PHP版本的处理存在几个关键差异,导致签名验证失败。下面是具体的问题分析和修正建议:
关键问题点
1. 错误使用HTML实体分隔符&而非&
在构造最终的SAS令牌字符串时,你用了&作为参数分隔符——这是HTML中的转义字符,但SAS令牌的参数应该用普通的&分隔。服务器会把&sig当成一个完整的参数名,而非解析为sig参数,这直接导致签名无法被正确识别,是引发错误的主要原因。
2. 重复对URI进行小写转换
你对URI做了两次toLowerCase()处理:
targetUri = URLEncoder.encode(uri.toString().toLowerCase(), "UTF-8").toLowerCase();
这可能会导致和PHP版本的URI编码结果不一致(比如PHP中可能只对原始URI做一次编码,没有额外转小写),进而影响签名的原始字符串内容,破坏签名一致性。
3. 签名的URL编码逻辑与PHP不一致
Base64编码后的签名包含+、/、=这些需要URL转义的字符,PHP的常用实现会直接替换为URL安全的版本(比如-代替+,_代替/,去掉=),而你的Java代码用URLEncoder.encode会把+转成%2B,/转成%2F,这也会导致签名不匹配。
修正后的generateSasToken方法
下面是调整后的代码,修复了上述所有问题:
private String generateSasToken(URI uri) { try { // 只对原始URI进行一次URL编码,保留原始大小写(URI本身大小写不敏感,无需额外转小写) String targetUri = URLEncoder.encode(uri.toString(), "UTF-8"); // 计算过期时间(Unix时间戳,单位秒) long expires = System.currentTimeMillis() / 1000 + SdkGlobalSettings.getAuthorizationTokenExpirationInMinutes() * 60; // 构造待签名的字符串 String toSign = targetUri + "\n" + expires; // 使用HMAC-SHA256生成签名(和PHP使用的算法保持一致) byte[] keyBytes = SasKeyValue.getBytes("UTF-8"); SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(signingKey); byte[] rawHmac = mac.doFinal(toSign.getBytes("UTF-8")); // 生成URL安全的Base64签名,和PHP处理逻辑对齐 String signature = Base64.encodeBase64String(rawHmac) .replace('+', '-') .replace('/', '_') .replaceAll("=", ""); // 构造SAS令牌,使用&作为参数分隔符 String token = String.format("SharedAccessSignature sr=%s&sig=%s&se=%d&skn=%s", targetUri, signature, expires, SasKeyName); System.out.println(token); return token; } catch (Exception e) { throw new RuntimeException("Failed to generate SAS token", e); } }
额外验证点
- 确保
SasKeyValue是Azure Portal中复制的完整SharedAccessKey,没有多余的空格或换行符(既然PHP能运行,这个大概率没问题,但还是确认下)。 - 确认
SasKeyName和你使用的SharedAccessKeyName完全一致(比如你代码里的DefaultFullSharedAccessSignature)。 - 检查Notification Hub的名称和Endpoint是否和PHP代码中使用的完全匹配。
这些调整应该能让Java版本的SAS令牌生成逻辑和PHP对齐,解决签名无效的问题。
内容的提问来源于stack exchange,提问作者MicheleP
相关产品推荐
相关产品推荐

