Azure Blob存储错误显示并确保正确形成了授权标头的值,包括签名。
我正在使用RESTAPI使用访问密钥从Azure Blob存储中获取数据。 我有一个邮政集合,可以使用访问密钥来生成Azure的签名字符串。
但是,当我以Java语言实现相同的逻辑时,它会失败。 它给我带来了错误“ 403服务器未能对请求进行身份验证。确保正确形成了授权标头的值,包括签名。:[AuthenticationFailed
服务器无法对请求进行身份验证。确保正确形成授权标头的值...(435个字节)]“
这是生成签名字符串的Postman测试PER REQ部分。
// Should be UTC GMT string
pm.environment.set("utcStr", new Date().toUTCString());
// Get hash of all header-name:value
const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });
// Construct Signature value for Authorization header
var signatureParts = [
pm.request.method.toUpperCase(),
headers["content-encoding"] || "",
headers["content-language"] || "",
headers["content-length"] || "",
// pm.request.body ? pm.request.body.toString().length || "" : "",
headers["content-md5"] || "",
headers["content-type"] || "",
headers["x-ms-date"] ? "" : (pm.environment.get("utcStr") || ""),
headers["if-modified-since"] || "",
headers["if-match"] || "",
headers["if-none-match"] || "",
headers["if-unmodified-since"] || "",
headers["range"] || ""
];
// Construct CanonicalizedHeaders
const canonicalHeaderNames = [];
Object.keys(headers).forEach(key => {
if (key.startsWith("x-ms-")) {
canonicalHeaderNames.push(key);
}
});
// Sort headers lexographically by name
canonicalHeaderNames.sort();
const canonicalHeaderParts = [];
canonicalHeaderNames.forEach(key => {
let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];
// Populate variables
value = pm.environment.replaceIn(value);
// Replace whitespace in value but not if its within quotes
if (!value.startsWith("\"")) {
value = value.replace(/\s+/, " ");
}
canonicalHeaderParts.push(`${key}:${value}`);
});
// Add headers to signature
signatureParts.push.apply(signatureParts, canonicalHeaderParts);
// Construct CanonicalizedResource
const canonicalResourceParts = [
`/${pm.environment.get("storeAcc")}${pm.request.url.getPath()}`
];
const canonicalQueryNames = [];
pm.request.url.query.each(query => {
canonicalQueryNames.push(query.key.toLowerCase());
});
canonicalQueryNames.sort();
canonicalQueryNames.forEach(queryName => {
const value = pm.request.url.query.get(queryName);
// NOTE: This does not properly explode multiple same query params' values
// and turn them into comma-separated list
canonicalResourceParts.push(`${queryName}:${value}`);
});
// Add resource to signature
signatureParts.push.apply(signatureParts, canonicalResourceParts);
console.log("Signature Parts", signatureParts);
// Now, construct signature raw string
const signatureRaw = signatureParts.join("\n");
console.log("Signature String", JSON.stringify(signatureRaw));
// Hash it using HMAC-SHA256 and then encode using base64
const storageKey = pm.variables.get("accountKey");
const signatureBytes = CryptoJS.HmacSHA256(signatureRaw, CryptoJS.enc.Base64.parse(storageKey));
console.log("signatureBytes",signatureBytes);
const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);
console.log("signatureEncoded",signatureEncoded);
console.log("Storage Account", pm.environment.get("storeAcc"));
console.log("Storage Key", storageKey);
// Finally, make it available for headers
pm.environment.set("sigStr",
`SharedKey ${pm.environment.get("storeAcc")}:${signatureEncoded}`);
该代码有效,我生成的签名已进行身份验证,我可以访问BLOB API。
但是我在Java中实现了相同的代码,每次给我一个相同的问题“ 403身份验证错误”。
java代码
public String generateOauthHeader(String method, UserDetails userFields, String baseUrl, String azureFileListUrl) {
String key = userFields.getAccountKey();
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String startDate = dateFormat.format(new Date());
String stringToSign = method + "\\n\\n\\n\\n\\napplication/json\\n\\n\\n\\n\\n\\n\\nx-ms-date:" + "Tue, 05 Jul 2022 11:45:00 "
+ "GMT\\nx-ms-version:2020-04-08\\n/" + userFields.getAccountName() + "/"
+ userFields.getContainerName();
if (null != azureFileListUrl) {
azureFileListUrl =azureFileListUrl.replace("?", "");
azureFileListUrl =azureFileListUrl.replace("=", ":");
String[] paramlist = azureFileListUrl.split("&");
stringToSign = stringToSign + "\\n" + paramlist[1] + "\\n" + paramlist[0];
}
stringToSign="\""+stringToSign+"\"";
System.err.println("Signature string = " + stringToSign);
String signature = getHMAC256(key, stringToSign);
signature = "SharedKey " + userFields.getAccountName() + ":" + signature;
System.err.println("Final Signature string = " + signature);
return signature;
}
private static String getHMAC256(String accountKey, String signStr) {
String signature = null;
try {
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(accountKey), "HmacSHA256");
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
sha256HMAC.init(secretKey);
signature = Base64.getEncoder().encodeToString(sha256HMAC.doFinal(signStr.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
我在下面添加了大约同时生成的两个字符串。
签名前的样品签名是相同的。我也打印并检查了它们。
postman= "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:45:00 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"
code = "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:44:56 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"
编辑: 这是带有请求的标题。
requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");
requestHeaders.put("x-ms-version", "2020-04-08");
requestHeaders.put("Content-Type", "application/json");
requestHeaders.put("Authorization", "SharedKey accountName:yvfdsfdssdfF0lGGtEWGU+b7BqFY0UHMkvI=
");
I am using restapi to fetch data from azure blob storage using the access keys.
I have a postman collection which i can use to generate the signature string for the azure using the access keys.
But when i implement the same logic in java language it fails.
It is giving me the error "403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.: [AuthenticationFailed
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly inclu... (435 bytes)]"
here is the postman test pre req part which generate the signature string.
// Should be UTC GMT string
pm.environment.set("utcStr", new Date().toUTCString());
// Get hash of all header-name:value
const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });
// Construct Signature value for Authorization header
var signatureParts = [
pm.request.method.toUpperCase(),
headers["content-encoding"] || "",
headers["content-language"] || "",
headers["content-length"] || "",
// pm.request.body ? pm.request.body.toString().length || "" : "",
headers["content-md5"] || "",
headers["content-type"] || "",
headers["x-ms-date"] ? "" : (pm.environment.get("utcStr") || ""),
headers["if-modified-since"] || "",
headers["if-match"] || "",
headers["if-none-match"] || "",
headers["if-unmodified-since"] || "",
headers["range"] || ""
];
// Construct CanonicalizedHeaders
const canonicalHeaderNames = [];
Object.keys(headers).forEach(key => {
if (key.startsWith("x-ms-")) {
canonicalHeaderNames.push(key);
}
});
// Sort headers lexographically by name
canonicalHeaderNames.sort();
const canonicalHeaderParts = [];
canonicalHeaderNames.forEach(key => {
let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];
// Populate variables
value = pm.environment.replaceIn(value);
// Replace whitespace in value but not if its within quotes
if (!value.startsWith("\"")) {
value = value.replace(/\s+/, " ");
}
canonicalHeaderParts.push(`${key}:${value}`);
});
// Add headers to signature
signatureParts.push.apply(signatureParts, canonicalHeaderParts);
// Construct CanonicalizedResource
const canonicalResourceParts = [
`/${pm.environment.get("storeAcc")}${pm.request.url.getPath()}`
];
const canonicalQueryNames = [];
pm.request.url.query.each(query => {
canonicalQueryNames.push(query.key.toLowerCase());
});
canonicalQueryNames.sort();
canonicalQueryNames.forEach(queryName => {
const value = pm.request.url.query.get(queryName);
// NOTE: This does not properly explode multiple same query params' values
// and turn them into comma-separated list
canonicalResourceParts.push(`${queryName}:${value}`);
});
// Add resource to signature
signatureParts.push.apply(signatureParts, canonicalResourceParts);
console.log("Signature Parts", signatureParts);
// Now, construct signature raw string
const signatureRaw = signatureParts.join("\n");
console.log("Signature String", JSON.stringify(signatureRaw));
// Hash it using HMAC-SHA256 and then encode using base64
const storageKey = pm.variables.get("accountKey");
const signatureBytes = CryptoJS.HmacSHA256(signatureRaw, CryptoJS.enc.Base64.parse(storageKey));
console.log("signatureBytes",signatureBytes);
const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);
console.log("signatureEncoded",signatureEncoded);
console.log("Storage Account", pm.environment.get("storeAcc"));
console.log("Storage Key", storageKey);
// Finally, make it available for headers
pm.environment.set("sigStr",
`SharedKey ${pm.environment.get("storeAcc")}:${signatureEncoded}`);
This code works and the signature i generate is authenticated and i get access to blob apis.
but i implemented the same code in java and every time it gives me the same problem "403 Authentication error."
Java code
public String generateOauthHeader(String method, UserDetails userFields, String baseUrl, String azureFileListUrl) {
String key = userFields.getAccountKey();
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String startDate = dateFormat.format(new Date());
String stringToSign = method + "\\n\\n\\n\\n\\napplication/json\\n\\n\\n\\n\\n\\n\\nx-ms-date:" + "Tue, 05 Jul 2022 11:45:00 "
+ "GMT\\nx-ms-version:2020-04-08\\n/" + userFields.getAccountName() + "/"
+ userFields.getContainerName();
if (null != azureFileListUrl) {
azureFileListUrl =azureFileListUrl.replace("?", "");
azureFileListUrl =azureFileListUrl.replace("=", ":");
String[] paramlist = azureFileListUrl.split("&");
stringToSign = stringToSign + "\\n" + paramlist[1] + "\\n" + paramlist[0];
}
stringToSign="\""+stringToSign+"\"";
System.err.println("Signature string = " + stringToSign);
String signature = getHMAC256(key, stringToSign);
signature = "SharedKey " + userFields.getAccountName() + ":" + signature;
System.err.println("Final Signature string = " + signature);
return signature;
}
private static String getHMAC256(String accountKey, String signStr) {
String signature = null;
try {
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(accountKey), "HmacSHA256");
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
sha256HMAC.init(secretKey);
signature = Base64.getEncoder().encodeToString(sha256HMAC.doFinal(signStr.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
I am adding below two string which are generated around same time.
The sample signatureString before signing are identical. I have printed and checked them as well.
postman= "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:45:00 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"
code = "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:44:56 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"
EDIT:
here are the headers sent with the request.
requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");
requestHeaders.put("x-ms-version", "2020-04-08");
requestHeaders.put("Content-Type", "application/json");
requestHeaders.put("Authorization", "SharedKey accountName:yvfdsfdssdfF0lGGtEWGU+b7BqFY0UHMkvI=
");
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我相信问题是以下代码行:
本质上是在您的请求标题中,您将日期发送为
tue,05 Jul 2022 11:45:00 GMT
gmt ,但是在您的StringToSign中< /code>您将日期发送为
TUE,05 JUL 2022 11:44:56 GMT
inX-MS-DATE
。这将导致您要发送的StringTosign
,并且服务器计算为不匹配,从而导致不同的授权标题值。要解决此问题,请确保
X-MS-DATE
标题的值在StringTosign
和您的请求标头中都是相同的。我建议将日期对象传递到generateOauthheader
方法,并在您的请求标头中使用X-MS-DATE
的同一日期对象。I believe the issue is with the following line of code:
Essentially in your request headers, you are sending the date as
Tue, 05 Jul 2022 11:45:00 GMT
however in yourstringToSign
you are sending the date asTue, 05 Jul 2022 11:44:56 GMT
inx-ms-date
. This would cause thestringToSign
that you are sending and the one computed by the server to mismatch and thus resulting in different authorization header values.To fix this, please ensure that the value for
x-ms-date
header is the same in bothstringToSign
and your request headers. I would recommend passing the date object togenerateOauthHeader
method and use the same date object in your request headers forx-ms-date
.