Last active
November 26, 2021 16:42
-
-
Save l-7-l/5d6d48e733a444043439d8e5bb4aaebd to your computer and use it in GitHub Desktop.
the provided credentials could not be validated. Please check your signature is correct.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use chrono::{DateTime, Local}; | |
use crypto_hash::{hex_digest, Algorithm}; | |
use hmac::crypto_mac::Output; | |
use hmac::{Hmac, Mac, NewMac}; | |
use serde::{Deserialize, Serialize}; | |
use sha2::Sha256; | |
#[derive(Serialize, Deserialize, Debug, Clone)] | |
pub struct TencentSms { | |
request_host: String, | |
request_content_type: String, | |
http_request_method: String, | |
request_params: SmsRequestParams, | |
now_at: DateTime<Local>, | |
service: String, | |
sign_method: String, | |
request_type: String, | |
secret_id: String, | |
secret_key: String, | |
} | |
impl TencentSms { | |
pub fn new( | |
secret_id: &str, | |
secret_key: &str, | |
sdk_app_id: &str, | |
template_id: &str, | |
sign: &str, | |
phones: Vec<String>, | |
template_params: Vec<String>, | |
) -> Self { | |
let sms_request_params = SmsRequestParams::new( | |
phones, | |
template_id.to_string(), | |
sign.to_string(), | |
template_params, | |
sdk_app_id.to_string(), | |
); | |
Self { | |
request_host: String::from("sms.tencentcloudapi.com"), | |
request_content_type: String::from( | |
"application/json; charset=utf-8", | |
), | |
http_request_method: String::from("POST"), | |
request_params: sms_request_params, | |
now_at: Local::now(), | |
service: String::from("sms"), | |
sign_method: String::from("TC3-HMAC-SHA256"), | |
request_type: String::from("tc3_request"), | |
secret_id: secret_id.to_string(), | |
secret_key: secret_key.to_string(), | |
} | |
} | |
fn get_request_payload(&self) -> String { | |
match self.http_request_method.as_str() { | |
"POST" => { | |
let payload = | |
serde_json::to_string(&self.request_params).unwrap(); | |
hex_digest(Algorithm::SHA256, payload.as_bytes()) | |
} | |
"GET" => String::new(), | |
_ => String::new(), | |
} | |
} | |
fn get_canonical_request(&self) -> String { | |
let request_payload = self.get_request_payload(); | |
let canonical_uri = "/"; | |
let canonical_query_string = ""; | |
let canonical_request = format!( | |
"{}\n{}\n{}\ncontent-type:{}\nhost:{}\ncontent-type;host\n{}", | |
self.http_request_method, // "POST" | |
canonical_uri, // "/" | |
canonical_query_string, // "" | |
self.request_content_type, // "application/json; charset=utf-8" | |
self.request_host, | |
request_payload | |
); | |
tracing::info!("canonical_request {}", &canonical_request); | |
hex_digest(Algorithm::SHA256, canonical_request.as_bytes()) | |
} | |
fn signature(&self) -> String { | |
let timestamp = self.now_at.timestamp(); | |
let date = self.get_date(); | |
let canonical_request = self.get_canonical_request(); | |
let signed_string = format!( | |
"{}\n{}\n{}/{}/{}\n{}", | |
self.sign_method, | |
×tamp, | |
self.get_date(), | |
self.service, | |
self.request_type, | |
canonical_request | |
); | |
tracing::info!("signed_string: {}", signed_string); | |
let secret_date = self.hmac( | |
format!("TC3{}", self.secret_key).as_bytes(), | |
date.as_bytes(), | |
); | |
let secret_service = | |
self.hmac(&*secret_date.into_bytes(), self.service.as_bytes()); | |
let secret_signing = self | |
.hmac(&*secret_service.into_bytes(), self.request_type.as_bytes()); | |
let signature_sha256 = | |
self.hmac(&*secret_signing.into_bytes(), signed_string.as_bytes()); | |
hex::encode(signature_sha256.into_bytes()) | |
} | |
fn authorization(&self) -> String { | |
let authorization = format!( | |
"{} Credential={}/{}/{}/{}, SignedHeaders=content-type;host, Signature={}", | |
self.sign_method, | |
self.secret_id, | |
self.get_date(), | |
self.service, | |
self.request_type, | |
self.signature() | |
); | |
authorization | |
} | |
async fn request(&self) { | |
let url = "https://sms.tencentcloudapi.com/"; | |
let request_params = self.request_params.get_body_or_query_string(); | |
let response: serde_json::Value = reqwest::Client::new() | |
.post(url) | |
.header("Authorization", self.authorization()) | |
.header("Content-Type", self.request_content_type.clone()) | |
.header("Host", self.request_host.clone()) | |
.header("X-TC-Action", "SendSms") | |
.header("X-TC-Timestamp", self.now_at.timestamp().to_string()) | |
.header("X-TC-Version", "2021-01-11") | |
.header("X-TC-Region", "ap-guangzhou") | |
.json(request_params) | |
.send() | |
.await | |
.unwrap() | |
.json() | |
.await | |
.unwrap(); | |
tracing::info!("{:?}", self.authorization()); | |
tracing::info!("{:?}", response); | |
// match response { | |
// Ok(mut res) => { | |
// let body = res.body().await.map_err(|e| "读取响应内容失败")?; | |
// tracing::debug!("响应内容: {:?}", &body); | |
// let json = serde_json::from_slice(&body); | |
// match json { | |
// Ok(response) => Ok(response), | |
// Err(e) => { | |
// tracing::error!("发送短信时解析响应出现错误: {:?}", e); | |
// Err("发送短信时解析响应出现错误") | |
// } | |
// } | |
// } | |
// Err(e) => { | |
// tracing::error!("发送短信时发送错误: {:?}", e); | |
// Err("发送短信时发送错误") | |
// } | |
// } | |
} | |
pub async fn send(&self) { | |
self.request().await | |
} | |
fn hmac(&self, key: &[u8], content: &[u8]) -> Output<Hmac<Sha256>> { | |
type HmacSha256 = Hmac<Sha256>; | |
let mut mac = HmacSha256::new_from_slice(key) | |
.expect("HMAC can take key of any size"); | |
mac.update(content); | |
mac.finalize() | |
} | |
fn get_date(&self) -> String { | |
self.now_at.naive_utc().date().to_string() | |
} | |
} | |
#[derive(Serialize, Deserialize, Debug, Clone)] | |
struct SmsRequestParams { | |
/*#[serde(rename(serialize = "Action"))] | |
action: String,*/ | |
#[serde(rename(serialize = "PhoneNumberSet"))] | |
phone_number_set: Vec<String>, | |
#[serde(rename(serialize = "TemplateId"))] | |
template_id: String, | |
#[serde(rename(serialize = "SignName"))] | |
sign_name: String, | |
#[serde(rename(serialize = "TemplateParamSet"))] | |
template_param_set: Vec<String>, | |
#[serde(rename(serialize = "SmsSdkAppId"))] | |
sms_sdk_app_id: String, | |
} | |
impl SmsRequestParams { | |
pub fn new( | |
phone_number_set: Vec<String>, | |
template_id: String, | |
sign_name: String, | |
template_param_set: Vec<String>, | |
sms_sdk_app_id: String, | |
) -> Self { | |
Self { | |
phone_number_set, | |
template_id, | |
sign_name, | |
template_param_set, | |
sms_sdk_app_id, | |
} | |
} | |
pub fn get_body_or_query_string(&self) -> &SmsRequestParams { | |
self | |
} | |
} | |
#[derive(Serialize, Deserialize, Debug, Clone)] | |
pub struct Response { | |
#[serde(rename(deserialize = "Response"))] | |
response: ResponseSub, | |
} | |
impl Response { | |
pub fn check_is_success(&self, mobile: String) -> bool { | |
for send_status in self.response.send_status_set.clone().into_iter() { | |
if send_status.phone_number == mobile { | |
return send_status.code == String::from("Ok"); | |
} | |
} | |
false | |
} | |
} | |
#[derive(Serialize, Deserialize, Debug, Clone)] | |
struct ResponseSub { | |
#[serde(rename(deserialize = "SendStatusSet"))] | |
send_status_set: Vec<ResponseSubItem>, | |
#[serde(rename(deserialize = "RequestId"))] | |
request_id: String, | |
} | |
#[derive(Serialize, Deserialize, Debug, Clone)] | |
struct ResponseSubItem { | |
#[serde(rename(deserialize = "SerialNo"))] | |
seria_no: String, | |
#[serde(rename(deserialize = "PhoneNumber"))] | |
phone_number: String, | |
#[serde(rename(deserialize = "Fee"))] | |
fee: i64, | |
#[serde(rename(deserialize = "SessionContext"))] | |
session_context: String, | |
#[serde(rename(deserialize = "Code"))] | |
code: String, | |
#[serde(rename(deserialize = "Message"))] | |
message: String, | |
#[serde(rename(deserialize = "IsoCode"))] | |
iso_code: String, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
依赖都是最新的.
对着文档检查了几遍没发现错在哪
tencent_sms 替换了该包的http client