diff --git a/CLAUDE.md b/CLAUDE.md index e1e0600..45ffae0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -136,7 +136,7 @@ npm run import # 导入设备数据 - **Web UI(v2.1)**:`middleware.ts` 优先检查 `tlyq_session`(共享 JWT,OA 统一签发)→ 回退 `session_assets`(本地 JWT)。`getSession()` 每次请求时检查 LLDAP 用户是否存在,已删除则清除 cookie 踢出 - **localadmin**:纯本地 BCrypt 认证,不依赖 LLDAP,用于 LLDAP 故障时应急登录(DB 预置,admin 角色) -- **API Key(v2.2)**:`middleware.ts` 采用两级验证:① `ALLOWED_API_KEYS` 环境变量快速匹配(逗号分隔明文 key);② 未命中则查 `api_keys` 数据库表(SHA-256 hash)。无效 key 在中间件层直接返回 401(此前仅检查 `Bearer ak_` 前缀就放行,实际验证延后到 route handler)。外部系统(如 issue-ai)调用本系统 API 时,key 可注册在 `ALLOWED_API_KEYS` 或 `api_keys` 表中任意一处即可 +- **API Key(v2.2)**:`middleware.ts` 检查 `ALLOWED_API_KEYS` 环境变量(逗号分隔明文 key),无效 key 在中间件层直接返回 401。注意:middleware 运行在 Edge Runtime,不能使用 `better-sqlite3`,DB 级别的 key 验证由 route handler 中的 `auth.ts verifyApiKey()` 进行(查 `api_keys` 表 SHA-256 hash,支持 `last_used_at` 追踪和 permissions 控制)。外部系统调用本系统 API 时,key 必须注册在 `ALLOWED_API_KEYS` 中 --- diff --git a/src/middleware.ts b/src/middleware.ts index cde6ca9..c5802f6 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,23 +1,14 @@ import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' -import crypto from 'crypto' -import db from '@/lib/db' -// API Key 验证:ALLOWED_API_KEYS 环境变量(快速路径)→ api_keys 数据库表(回退) +// API Key 验证:检查 ALLOWED_API_KEYS 环境变量(逗号分隔明文 key) +// 注意:middleware 运行在 Edge Runtime,不能使用 better-sqlite3 等 Node.js 原生模块 +// DB 级别的 key 验证在 route handler 中进行(auth.ts verifyApiKey) function verifyApiKey(key: string): boolean { if (!key.startsWith('ak_')) return false - // 1. 快速路径:ALLOWED_API_KEYS 环境变量 const allowedKeys = process.env.ALLOWED_API_KEYS || '' - if (allowedKeys && allowedKeys.split(',').map(k => k.trim()).includes(key)) { - return true - } - // 2. 回退:查 api_keys 数据库表 - try { - const keyHash = crypto.createHash('sha256').update(key).digest('hex') - const row = db.prepare('SELECT id FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) - if (row) return true - } catch { /* DB 不可用时忽略 */ } - return false + if (!allowedKeys) return false + return allowedKeys.split(',').map(k => k.trim()).includes(key) } function decodeJwtPayload(token: string): Record | null {