feat(mp): S3-1 API 请求签名工具(前端,待后端集成)
- 新增 request-signer.ts:HMAC-SHA256 签名 + nonce + timestamp - 使用 @noble/hashes v1 纯 JS 实现(小程序无 crypto.subtle) - 签名密钥仅存内存(setSigningKey/clearSigningKey) - 8 个单元测试覆盖签名生成 + nonce + HMAC - 集成到 request.ts 待后端 signing_key 支持后启用
This commit is contained in:
65
apps/miniprogram/src/utils/request-signer.ts
Normal file
65
apps/miniprogram/src/utils/request-signer.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
/** HMAC-SHA256 同步签名,返回十六进制字符串 */
|
||||
export function hmacSha256Sync(key: string, message: string): string {
|
||||
const encoder = new TextEncoder();
|
||||
const mac = hmac(sha256, encoder.encode(key), encoder.encode(message));
|
||||
return Array.from(mac)
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
}
|
||||
|
||||
/** 生成 16 字符随机 nonce(十六进制) */
|
||||
export function generateNonce(): string {
|
||||
const arr = new Uint8Array(8);
|
||||
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
||||
crypto.getRandomValues(arr);
|
||||
} else {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
}
|
||||
return Array.from(arr)
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
}
|
||||
|
||||
/** 为 API 请求生成 HMAC-SHA256 签名头 */
|
||||
export function signRequest(
|
||||
method: string,
|
||||
path: string,
|
||||
body: unknown,
|
||||
signingKey: string,
|
||||
): Record<string, string> {
|
||||
const timestamp = String(Math.floor(Date.now() / 1000));
|
||||
const nonce = generateNonce();
|
||||
|
||||
const bodyStr = body !== undefined ? JSON.stringify(body) : '';
|
||||
const bodyHash = bodyStr ? hmacSha256Sync(signingKey, bodyStr) : '';
|
||||
|
||||
const message = `${method.toUpperCase()}${path}${bodyHash}${timestamp}${nonce}`;
|
||||
const signature = hmacSha256Sync(signingKey, message);
|
||||
|
||||
return {
|
||||
'X-Signature': signature,
|
||||
'X-Timestamp': timestamp,
|
||||
'X-Nonce': nonce,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 签名密钥管理(仅内存,不持久化) ───
|
||||
|
||||
let _signingKey = '';
|
||||
|
||||
export function setSigningKey(key: string): void {
|
||||
_signingKey = key;
|
||||
}
|
||||
|
||||
export function clearSigningKey(): void {
|
||||
_signingKey = '';
|
||||
}
|
||||
|
||||
export function getSigningKey(): string {
|
||||
return _signingKey;
|
||||
}
|
||||
Reference in New Issue
Block a user