我已经将文档中的Python示例翻译成了Rust,该示例介绍了如何为向数据收集器API发出的请求构造Authorization头中所需的签名。我的Rust程序生成了一个SharedKey <workspace_id>:<signature>
,它被服务器拒绝,代码为403,响应的主体只是说:
{\\\"Error\\\":\\\"InvalidAuthorization\\\",\\\"Message\\\":\\\"An invalid signature was specified in the Authorization header\\\"}
另一方面,我从Rust程序执行中获取了RFC1123格式的相同日期,并将其硬编码到Python示例脚本中。它为authorization头生成 * 完全 * 相同的SharedKey <workspace_id>:<signature>
值,并成功使用API发送日志。
响应中没有关于Rust程序签名问题性质的其他信息。据我所知,错误可能是报告了错误的问题,因为它们是完全相同的签名,一个工作,而另一个不工作。
此API返回此错误类型的其他原因是什么?
如果有人认为有用,我很乐意分享一些Rust代码,但我确信他们文档中的示例Python脚本和我的Rust程序中的授权头是相同的。所以我觉得这是别的东西。
- 编辑:* 这里是上面链接中稍微修改过的Python示例版本,它成功地使用了API,我看到测试消息显示在日志分析工作区中:
import requests
import datetime
import hashlib
import hmac
import base64
WORKSPACE_ID = "<my workspace ID>"
SHARED_KEY = "<my primary key>"
def build_signature(message, secret):
key_bytes = base64.b64decode(secret)
message_bytes = bytes(message, encoding="utf-8")
hmacsha256 = hmac.new(key_bytes, message_bytes, digestmod=hashlib.sha256).digest()
encoded_hash = base64.b64encode(hmacsha256).decode()
return encoded_hash
def post_data():
data = '{"hello":"world"}'
date_string = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
# date_string = "Thu, 14 Sep 2023 01:27:12 GMT"
content_length = len(data)
string_to_hash = f"POST\n{content_length}\napplication/json\nx-ms-date:{date_string}\n/api/logs"
hashed_string = build_signature(string_to_hash, SHARED_KEY)
signature = f"SharedKey {WORKSPACE_ID}:{hashed_string}"
print(signature)
query = "api-version=2016-04-01";
url = f"https://{WORKSPACE_ID}.ods.opinsights.azure.com/api/logs?{query}"
headers = {
'content-type': "application/json",
'Authorization': signature,
'Log-Type': "my-event-type",
'x-ms-date': date_string
}
response = requests.post(url, data=data, headers=headers)
if (response.status_code >= 200 and response.status_code <= 299):
print('Accepted')
else:
print("Response: {}".format(response.content))
print("Response code: {}".format(response.status_code))
if __name__ == "__main__":
post_data()
下面是我的Rust版本,它产生了完全相同的授权头值,但在尝试使用它时出现了InvalidAuthorization
错误:
use base64;
use chrono::Utc;
use ring::hmac;
#[derive(Clone, Debug, Error)]
enum MyError {
MessageSendError(String),
}
struct SentinelClient {
azure_collector_url: String,
workspace_id: String,
shared_key: String,
http_client: reqwest::Client,
}
impl SentinelClient {
fn new(workspace_id: String, shared_key: String) -> Self {
let query = "api-version=2016-04-01";
let azure_collector_url =
format!("https://{workspace_id}.ods.opinsights.azure.com/api/logs?{query}");
Self {
azure_collector_url,
workspace_id,
shared_key,
http_client: reqwest::Client::new(),
}
}
fn send(&self, data: String) -> Result<(), MyError> {
let workspace_id = &self.workspace_id;
let date_string = format!("{}", Utc::now().format("%a, %d %b %Y %H:%M:%S GMT"));
println!("date string {}", date_string);
let content_length = data.to_string().len();
let string_to_hash =
format!("POST\n{content_length}\napplication/json\nx-ms-date:{date_string}\n/api/logs");
let hashed_string = Self::build_signature(&string_to_hash, &self.shared_key)?;
let signature = format!("SharedKey {workspace_id}:{hashed_string}");
println!("{}", signature);
let url = &self.azure_collector_url;
let request = self
.http_client
.post(url)
.json(&data)
.header("Authorization", signature)
.header("Log-Type", "my-event-type")
.header("x-ms-date", date_string);
match request.send().await {
Ok(resp) => {
if resp.status().is_success() {
println!("request successful: {:?}", &resp);
Ok(())
} else {
let body = resp
.text()
.await
.map_err(|e| SinkError::MessageSendError(e.to_string()))?;
println!("request unsuccessful: {body}");
Err(MyError::MessageSendError(body))
}
}
Err(e) => Err(MyError::MessageSendError(e.to_string())),
}
/// Build the API signature for the Azure Monitor HTTP Data Collector
/// authorization header.
///
/// See https://learn.microsoft.com/en-us/azure/azure-monitor/logs/data-collector-api?tabs=python#authorization.
fn build_signature(message: &str, secret: &str) -> Result<String, MyError> {
let key_bytes = base64::decode(secret).map_err(|e| {
MyError::MessageSendError(format!("could not decode shared key: {e}"))
})?;
let message_bytes = message.as_bytes();
let hmacsha256 = hmac::Key::new(hmac::HMAC_SHA256, &key_bytes);
let encoded_hash = base64::encode(hmac::sign(&hmacsha256, message_bytes).as_ref());
Ok(encoded_hash)
}
}
1条答案
按热度按时间qcbq4gxm1#
我被告知JSON请求体必须是utf-8编码的,否则服务器会用403 Unauthorized拒绝请求,这似乎是令人难以置信的误导。在我的例子中,请求体是一个Rust
String
,表示一个JSON对象,它是 * utf-8编码的,我通过我还不清楚为什么这个会起作用,
因为我的印象是
json
方法正是这样做的。但是json
方法必须对数据的编码方式做一些body
不做的事情。