const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const crypto = require('crypto'); const prisma = require('./prisma'); const JWT_EXPIRES_IN = '7d'; const ADMIN_JWT_EXPIRES_IN = '7d'; // 安全:生产环境必须配置 JWT_SECRET,杜绝硬编码密钥 const JWT_SECRET = process.env.JWT_SECRET; if (!JWT_SECRET) { if (process.env.NODE_ENV === 'production') { throw new Error( 'JWT_SECRET 环境变量未设置。请在 .env 文件中配置一个随机密钥(可用 `openssl rand -hex 32` 生成)。' ); } console.warn('[安全警告] JWT_SECRET 未设置,使用开发环境临时密钥。请勿在生产环境使用。'); } const SECRET = JWT_SECRET || 'dev_only_insecure_secret_do_not_use_in_production'; // Admin JWT 使用独立 secret,防止跨角色伪造 const ADMIN_JWT_SECRET = process.env.ADMIN_JWT_SECRET || (process.env.NODE_ENV === 'production' ? null : 'dev_only_admin_insecure_secret'); // 哈希密码 function hashPassword(password) { return bcrypt.hashSync(password, 10); } // 验证密码 function verifyPassword(password, hash) { return bcrypt.compareSync(password, hash); } // 生成 JWT function signToken(userId) { return jwt.sign({ userId }, SECRET, { expiresIn: JWT_EXPIRES_IN }); } // 验证 JWT 并提取 userId function verifyToken(token) { try { const decoded = jwt.verify(token, SECRET); return decoded.userId; } catch { return null; } } // Express 中间件:验证 JWT function authMiddleware(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: '未登录' }); } const token = authHeader.slice(7); const userId = verifyToken(token); if (!userId) { return res.status(401).json({ error: '登录已过期,请重新登录' }); } req.userId = userId; next(); } // Express 中间件:验证 API Key(用于 Hermes 配置拉取) // 支持 JWT token 和 API Key(eak_ 前缀)两种认证方式 async function apiKeyMiddleware(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: '需要认证' }); } const token = authHeader.slice(7); // 如果是 API Key(eak_ 前缀),查数据库验证 if (token.startsWith('eak_')) { try { const keyHash = crypto.createHash('sha256').update(token).digest('hex'); const apiKey = await prisma.apiKey.findUnique({ where: { keyHash }, select: { id: true, userId: true }, }); if (!apiKey) { return res.status(401).json({ error: 'API Key 无效' }); } // 更新最后使用时间(不阻塞请求) prisma.apiKey.update({ where: { id: apiKey.id }, data: { lastUsedAt: new Date() }, }).catch((err) => console.warn('更新 API Key lastUsedAt 失败:', err)); req.userId = apiKey.userId; req.authMethod = 'apikey'; return next(); } catch (err) { console.error('API Key 验证失败:', err); return res.status(500).json({ error: '认证失败' }); } } // 否则尝试 JWT 认证 const userId = verifyToken(token); if (!userId) { return res.status(401).json({ error: '认证无效' }); } req.userId = userId; req.authMethod = 'jwt'; next(); } // ===== Admin 认证 ===== // 生成 Admin JWT function adminSignToken(adminId) { return jwt.sign({ adminId, role: 'admin' }, ADMIN_JWT_SECRET, { expiresIn: ADMIN_JWT_EXPIRES_IN }); } // 验证 Admin JWT function adminVerifyToken(token) { try { const decoded = jwt.verify(token, ADMIN_JWT_SECRET); if (decoded.role !== 'admin') return null; return decoded.adminId; } catch { return null; } } // Express 中间件:验证 Admin JWT function adminAuthMiddleware(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: '管理员未登录' }); } const token = authHeader.slice(7); const adminId = adminVerifyToken(token); if (!adminId) { return res.status(401).json({ error: '管理员登录已过期,请重新登录' }); } req.adminId = adminId; next(); } module.exports = { hashPassword, verifyPassword, signToken, verifyToken, authMiddleware, apiKeyMiddleware, adminSignToken, adminVerifyToken, adminAuthMiddleware, };