1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use chrono::Utc;
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::collections::HashMap;
use crate::IopClient;
type HmacSha256 = Hmac<Sha256>;
impl IopClient {
/// Construct a URL with the given base URL, query parameters, and signature.
///
/// # Arguments
///
/// * `base_url` - The base URL to use for the constructed URL.
/// * `params` - A `HashMap` containing the query parameters to include in the URL.
/// * `sign` - The signature to append to the URL.
///
/// # Returns
///
/// A `String` representing the constructed URL.
pub fn generate_url(
&self,
base_url: String,
params: HashMap<String, String>,
sign: String,
) -> String {
let mut url = String::from(base_url);
let mut first = true;
for (key, value) in params {
if first {
url.push_str(&format!("?{}={}", key, value));
first = false;
} else {
url.push_str(&format!("&{}={}", key, value));
}
}
url.push_str(&format!("&sign={}", sign));
url
}
/// Generates a signature based on the provided HTTP method and payload.
///
/// 拼接参数名与参数值 [官方文档](https://open.alibaba.com/doc/doc.htm?spm=a2o9m.11193535.0.0.55fb2f04MHBYoD&docId=107343&docType=1#/?docId=134)
///
/// This function sorts the payload by key, concatenates the keys and values,
/// and prepends the optional HTTP method if provided. It then generates an
/// HMAC-SHA256 hash of the resulting string using the app's secret key.
///
/// # Arguments
///
/// * `method` - An optional string representing the HTTP method to be included
/// in the signature calculation.
/// * `payload` - A `HashMap` containing the parameters to be signed, where
/// keys are parameter names and values are parameter values.
///
/// # Returns
///
/// A `String` representing the computed HMAC-SHA256 signature in hexadecimal format.
pub fn generate_sign(
&self,
method: Option<String>,
payload: HashMap<String, String>,
) -> String {
let mut sorted_vec: Vec<(&String, &String)> = payload.iter().collect();
sorted_vec.sort_by(|a, b| a.0.cmp(b.0));
let mut concatenated = match method {
Some(m) => String::from(m),
None => String::new(),
};
for (key, value) in sorted_vec {
concatenated.push_str(key);
concatenated.push_str(value);
}
self.generate_hmac_sha256(concatenated.as_bytes())
}
/// Generates an HMAC-SHA256 signature of the provided data.
///
/// The signature is generated by creating an HMAC context with the app's secret key,
/// updating the context with the provided data, and then finalizing the context to
/// obtain the signature. The signature is then converted to a hexadecimal string
/// and returned.
///
/// # Arguments
///
/// * `data` - The data to be signed, represented as a byte slice.
///
/// # Returns
///
/// A `String` representing the computed HMAC-SHA256 signature in hexadecimal format.
pub fn generate_hmac_sha256(&self, data: &[u8]) -> String {
let app_secret = self.app_secret.clone();
let mut mac = HmacSha256::new_from_slice(app_secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(data);
let result = mac.finalize();
let code_bytes = result.into_bytes();
format!("{:X}", code_bytes)
}
/// Builds a complete set of request parameters for API calls.
///
/// This function constructs a `HashMap` containing both common and business-specific
/// parameters required for API requests. It includes parameters such as `app_key`,
/// `timestamp`, `sign_method`, and language, and attempts to include an `access_token`
/// if available. The provided `params` are added to the map as business-specific
/// parameters.
///
/// # Arguments
///
/// * `params` - A `HashMap` containing business-specific parameters to be included
/// in the API request.
///
/// # Returns
///
pub async fn build_request_params(
&self,
params: HashMap<String, String>,
) -> HashMap<String, String> {
let now = Utc::now().timestamp_millis().to_string();
let mut map = HashMap::new();
map.insert("app_key".to_string(), self.appid.to_string());
map.insert("sign_method".to_string(), "sha256".to_string());
map.insert("simplify".to_string(), "true".to_string());
map.insert("timestamp".to_string(), now);
map.insert("language".to_string(), "en_US".to_string());
if let Ok(at) = self.get_access_token().await {
map.insert("access_token".to_string(), at.access_token.clone());
}
// 业务参数
for (key, value) in params {
map.insert(key, value);
}
map
}
}