This commit is contained in:
maojindao55
2025-03-25 19:58:39 +08:00
parent 487621a645
commit 051a7da4c6
12 changed files with 482 additions and 66 deletions

125
functions/utils/sms.ts Normal file
View File

@@ -0,0 +1,125 @@
interface AliyunSMSConfig {
accessKeyId: string;
accessKeySecret: string;
signName: string;
templateCode: string;
}
// 辅助函数:生成随机字符串
function generateNonce(length: number): string {
const array = new Uint8Array(length);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
// 辅助函数:计算 HMAC-SHA256V3 使用 SHA256
async function calculateHmacSha256(message: string, secret: string): Promise<string> {
const encoder = new TextEncoder();
const keyData = encoder.encode(secret);
const messageData = encoder.encode(message);
const key = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign(
'HMAC',
key,
messageData
);
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}
export async function sendSMS(phone: string, code: string, config: AliyunSMSConfig) {
const API_URL = 'https://dysmsapi.aliyuncs.com/';
const API_VERSION = '2017-05-25';
// 准备请求参数
const params = {
AccessKeyId: config.accessKeyId,
Action: 'SendSms',
Format: 'JSON', // 明确指定返回 JSON 格式
PhoneNumbers: phone,
SignName: config.signName,
TemplateCode: config.templateCode,
TemplateParam: JSON.stringify({ code }),
Version: API_VERSION,
SignatureMethod: 'HMAC-SHA1',
SignatureVersion: '1.0',
SignatureNonce: generateNonce(16),
Timestamp: new Date().toISOString()
};
// 参数排序
const sortedParams = Object.keys(params)
.sort()
.reduce((acc, key) => ({
...acc,
[key]: params[key]
}), {});
// 构建签名字符串
const canonicalizedQueryString = Object.entries(sortedParams)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)
.join('&');
const stringToSign = `GET&${encodeURIComponent('/')}&${encodeURIComponent(canonicalizedQueryString)}`;
// 计算签名
const signature = await calculateHmacSha1(stringToSign, `${config.accessKeySecret}&`);
// 构建最终的 URL
const finalUrl = `${API_URL}?${canonicalizedQueryString}&Signature=${encodeURIComponent(signature)}`;
// 发送请求
const response = await fetch(finalUrl, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
const responseText = await response.text();
let responseData;
try {
responseData = JSON.parse(responseText);
} catch (e) {
console.error('Response:', responseText);
throw new Error('Invalid response format from SMS service');
}
//console.log(responseData, finalUrl)
if (!response.ok || responseData.Code !== 'OK') {
throw new Error(responseData.Message || 'SMS send failed');
}
return responseData;
}
// 辅助函数:计算 HMAC-SHA1
async function calculateHmacSha1(message: string, secret: string): Promise<string> {
const encoder = new TextEncoder();
const keyData = encoder.encode(secret);
const messageData = encoder.encode(message);
const key = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-1' },
false,
['sign']
);
const signature = await crypto.subtle.sign(
'HMAC',
key,
messageData
);
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}