Skip to content

Instantly share code, notes, and snippets.

@l-7-l
Last active November 26, 2021 16:42
Show Gist options
  • Save l-7-l/5d6d48e733a444043439d8e5bb4aaebd to your computer and use it in GitHub Desktop.
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.
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,
&timestamp,
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,
}
@l-7-l
Copy link
Author

l-7-l commented Nov 26, 2021

依赖都是最新的.
对着文档检查了几遍没发现错在哪
tencent_sms 替换了该包的http client

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment