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:
66
apps/miniprogram/__tests__/utils/request-signer.test.ts
Normal file
66
apps/miniprogram/__tests__/utils/request-signer.test.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { signRequest, generateNonce, hmacSha256Sync } from '@/utils/request-signer';
|
||||
|
||||
describe('generateNonce', () => {
|
||||
it('生成 16 字符十六进制字符串', () => {
|
||||
const nonce = generateNonce();
|
||||
expect(nonce).toHaveLength(16);
|
||||
expect(nonce).toMatch(/^[0-9a-f]{16}$/);
|
||||
});
|
||||
|
||||
it('连续调用生成不同值', () => {
|
||||
const n1 = generateNonce();
|
||||
const n2 = generateNonce();
|
||||
expect(n1).not.toBe(n2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hmacSha256Sync', () => {
|
||||
it('相同输入产生相同输出', () => {
|
||||
const key = 'test-key';
|
||||
const msg = 'hello world';
|
||||
const r1 = hmacSha256Sync(key, msg);
|
||||
const r2 = hmacSha256Sync(key, msg);
|
||||
expect(r1).toBe(r2);
|
||||
expect(r1).toHaveLength(64); // SHA-256 = 32 bytes = 64 hex chars
|
||||
});
|
||||
|
||||
it('不同输入产生不同输出', () => {
|
||||
const r1 = hmacSha256Sync('key', 'msg1');
|
||||
const r2 = hmacSha256Sync('key', 'msg2');
|
||||
expect(r1).not.toBe(r2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('signRequest', () => {
|
||||
const signingKey = 'test-signing-key-256-bit!!!!!!!!!!!';
|
||||
|
||||
it('GET 请求生成正确的签名头', () => {
|
||||
const headers = signRequest('GET', '/health/patients', undefined, signingKey);
|
||||
|
||||
expect(headers).toHaveProperty('X-Signature');
|
||||
expect(headers).toHaveProperty('X-Timestamp');
|
||||
expect(headers).toHaveProperty('X-Nonce');
|
||||
expect(headers['X-Nonce']).toHaveLength(16);
|
||||
expect(headers['X-Signature']).toHaveLength(64);
|
||||
});
|
||||
|
||||
it('POST 请求包含 body hash', () => {
|
||||
const headers = signRequest('POST', '/health/vital-signs', { value: 120 }, signingKey);
|
||||
|
||||
expect(headers).toHaveProperty('X-Signature');
|
||||
expect(headers['X-Signature']).toHaveLength(64);
|
||||
});
|
||||
|
||||
it('无 body 时也能正常签名', () => {
|
||||
const headers = signRequest('GET', '/test', undefined, signingKey);
|
||||
expect(headers['X-Signature']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('相同参数不同 nonce 产生不同签名', () => {
|
||||
const h1 = signRequest('GET', '/test', undefined, signingKey);
|
||||
// 由于 nonce 不同,签名也不同
|
||||
const h2 = signRequest('GET', '/test', undefined, signingKey);
|
||||
expect(h1['X-Signature']).not.toBe(h2['X-Signature']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user