Files
maojindao55 051a7da4c6 add sms
2025-03-25 19:58:39 +08:00

125 lines
3.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)));
}