Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/guide/architecture/request-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 后,后续请求不会因为缓存命中而继续通过。缓存是单进程的,多实例之间不共享。

Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, number>();

Expand Down
6 changes: 3 additions & 3 deletions tests/unit/utils/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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 {
Expand Down
Loading