From 295bb139fd45f7f04708c24514f41e65f4eb5073 Mon Sep 17 00:00:00 2001 From: umaru Date: Thu, 27 Aug 2026 19:34:07 +0800 Subject: [PATCH] =?UTF-8?q?perf(auth):=20=E5=B0=86=20API=20Key=20=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E7=BC=93=E5=AD=98=20TTL=20=E5=BB=B6=E9=95=BF=E8=87=B3?= =?UTF-8?q?=E4=BA=94=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 API_KEY_VERIFY_CACHE_TTL_MS 从 120000 调整为 300000。 同步五分钟过期边界测试与鉴权生命周期文档。 验证:聚焦测试、全量测试、lint、format:check、tsc、build 均通过。 --- docs/guide/architecture/request-lifecycle.md | 2 +- src/lib/utils/auth.ts | 2 +- tests/unit/utils/auth.test.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/guide/architecture/request-lifecycle.md b/docs/guide/architecture/request-lifecycle.md index 58e3c237..cf8a434f 100644 --- a/docs/guide/architecture/request-lifecycle.md +++ b/docs/guide/architecture/request-lifecycle.md @@ -49,7 +49,7 @@ export async function POST(request: NextRequest, context: RouteContext) { ### 重复鉴权校验的性能边界 -`verifyApiKey` 对成功的 bcrypt 比对使用进程内短 TTL 缓存(当前 TTL 为 10 秒、最多保留 2048 条),缓存键由进程随机密钥保护的 HMAC-SHA-256 API Key 摘要与当前 bcrypt hash 组成,不保存 API Key 明文。首次请求或缓存失效时仍执行完整 bcrypt 比对。 +`verifyApiKey` 对成功的 bcrypt 比对使用进程内短 TTL 缓存(当前 TTL 为 5 分钟、最多保留 2048 条),缓存键由进程随机密钥保护的 HMAC-SHA-256 API Key 摘要与当前 bcrypt hash 组成,不保存 API Key 明文。首次请求或缓存失效时仍执行完整 bcrypt 比对。 该缓存不改变撤销和准入语义:代理每次请求仍先从数据库读取 `is_active` 的 Key 记录,并在缓存命中后继续检查过期时间、用户状态、模型权限、上游授权与速率 / 消费规则。停用或删除 Key 后,后续请求不会因为缓存命中而继续通过。缓存是单进程的,多实例之间不共享。 diff --git a/src/lib/utils/auth.ts b/src/lib/utils/auth.ts index 85d82e37..901299bb 100644 --- a/src/lib/utils/auth.ts +++ b/src/lib/utils/auth.ts @@ -15,7 +15,7 @@ const API_KEY_VERIFY_CACHE_KEY_PROMISE = webcrypto.subtle.importKey( // The proxy still loads the active key row before calling verifyApiKey, so this // cache only removes repeated bcrypt work; revocation, expiry, ownership, and // authorization changes remain database-authoritative on every request. -const API_KEY_VERIFY_CACHE_TTL_MS = 120_000; +const API_KEY_VERIFY_CACHE_TTL_MS = 5 * 60 * 1000; const API_KEY_VERIFY_CACHE_MAX_ENTRIES = 2_048; const apiKeyVerificationCache = new Map(); diff --git a/tests/unit/utils/auth.test.ts b/tests/unit/utils/auth.test.ts index 8abd1bfd..336b68da 100644 --- a/tests/unit/utils/auth.test.ts +++ b/tests/unit/utils/auth.test.ts @@ -61,7 +61,7 @@ describe("auth utilities", () => { const otherHash = await hashApiKey(otherKey); expect(await verifyApiKey(key, otherHash)).toBe(false); }); - it("should expire cached verification after the two-minute TTL", async () => { + it("should expire cached verification after the five-minute TTL", async () => { const key = "sk-auto-cache-expiring-key12345678901234567890"; const hash = await hashApiKey(key); const compareSpy = vi.spyOn(bcryptjs, "compare"); @@ -72,11 +72,11 @@ describe("auth utilities", () => { expect(await verifyApiKey(key, hash)).toBe(true); expect(compareSpy).toHaveBeenCalledTimes(1); - dateNowSpy.mockReturnValue(baseTime + 119_999); + dateNowSpy.mockReturnValue(baseTime + 299_999); expect(await verifyApiKey(key, hash)).toBe(true); expect(compareSpy).toHaveBeenCalledTimes(1); - dateNowSpy.mockReturnValue(baseTime + 120_000); + dateNowSpy.mockReturnValue(baseTime + 300_000); expect(await verifyApiKey(key, hash)).toBe(true); expect(compareSpy).toHaveBeenCalledTimes(2); } finally {