const jwt = require('jsonwebtoken'); const crypto = require('crypto'); const { UserDB } = require('./database'); const { decryptSecret } = require('./utils/encryption'); // JWT密钥(必须在环境变量中设置) const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; // Refresh Token密钥(使用不同的密钥) const REFRESH_SECRET = process.env.REFRESH_SECRET || JWT_SECRET + '-refresh'; // Token有效期配置 const ACCESS_TOKEN_EXPIRES = '2h'; // Access token 2小时 const REFRESH_TOKEN_EXPIRES = '7d'; // Refresh token 7天 // 安全检查:验证JWT密钥配置 const DEFAULT_SECRETS = [ 'your-secret-key-change-in-production', 'your-secret-key-change-in-production-PLEASE-CHANGE-THIS' ]; // 安全修复:增强 JWT_SECRET 验证逻辑 if (DEFAULT_SECRETS.includes(JWT_SECRET)) { const errorMsg = ` ╔═══════════════════════════════════════════════════════════════╗ ║ ⚠️ 安全警告 ⚠️ ║ ╠═══════════════════════════════════════════════════════════════╣ ║ JWT_SECRET 使用默认值,存在严重安全风险! ║ ║ ║ ║ 请立即设置环境变量 JWT_SECRET ║ ║ 生成随机密钥: ║ ║ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" ║ ║ ║ 在 backend/.env 文件中设置: ║ ║ JWT_SECRET=你生成的随机密钥 ║ ╚═══════════════════════════════════════════════════════════════╝ `; // 安全修复:无论环境如何,使用默认 JWT_SECRET 都拒绝启动 console.error(errorMsg); throw new Error('使用默认 JWT_SECRET 存在严重安全风险,服务无法启动!'); } // 验证 JWT_SECRET 长度(至少 32 字节/64个十六进制字符) if (JWT_SECRET.length < 32) { const errorMsg = ` ╔═══════════════════════════════════════════════════════════════╗ ║ ⚠️ 配置错误 ⚠️ ║ ╠═══════════════════════════════════════════════════════════════╣ ║ JWT_SECRET 长度不足! ║ ║ ║ ║ 要求: 至少 32 字节 ║ ║ 当前长度: ${JWT_SECRET.length} 字节 ║ ║ ║ ║ 生成安全的随机密钥: ║ ║ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" ╚═══════════════════════════════════════════════════════════════╝ `; console.error(errorMsg); throw new Error('JWT_SECRET 长度不足,服务无法启动!'); } console.log('[安全] ✓ JWT密钥验证通过'); // 生成Access Token(短期) function generateToken(user) { return jwt.sign( { id: user.id, username: user.username, is_admin: user.is_admin, type: 'access' }, JWT_SECRET, { expiresIn: ACCESS_TOKEN_EXPIRES } ); } // 生成Refresh Token(长期) function generateRefreshToken(user) { return jwt.sign( { id: user.id, type: 'refresh', // 添加随机标识,使每次生成的refresh token不同 jti: crypto.randomBytes(16).toString('hex') }, REFRESH_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRES } ); } // 验证Refresh Token并返回新的Access Token function refreshAccessToken(refreshToken) { try { const decoded = jwt.verify(refreshToken, REFRESH_SECRET); if (decoded.type !== 'refresh') { return { success: false, message: '无效的刷新令牌类型' }; } const user = UserDB.findById(decoded.id); if (!user) { return { success: false, message: '用户不存在' }; } if (user.is_banned) { return { success: false, message: '账号已被封禁' }; } if (!user.is_active) { return { success: false, message: '账号未激活' }; } // 生成新的access token const newAccessToken = generateToken(user); return { success: true, token: newAccessToken, user: { id: user.id, username: user.username, is_admin: user.is_admin } }; } catch (error) { if (error.name === 'TokenExpiredError') { return { success: false, message: '刷新令牌已过期,请重新登录' }; } return { success: false, message: '无效的刷新令牌' }; } } // 验证Token中间件 function authMiddleware(req, res, next) { // 从请求头或HttpOnly Cookie获取token(不再接受URL参数以避免泄露) const token = req.headers.authorization?.replace('Bearer ', '') || req.cookies?.token; if (!token) { return res.status(401).json({ success: false, message: '未提供认证令牌' }); } try { const decoded = jwt.verify(token, JWT_SECRET); const user = UserDB.findById(decoded.id); if (!user) { return res.status(401).json({ success: false, message: '用户不存在' }); } if (user.is_banned) { return res.status(403).json({ success: false, message: '账号已被封禁' }); } if (!user.is_active) { return res.status(403).json({ success: false, message: '账号未激活' }); } // 将用户信息附加到请求对象(包含所有存储相关字段) req.user = { id: user.id, username: user.username, email: user.email, is_admin: user.is_admin, // OSS存储字段(v3.0新增) has_oss_config: user.has_oss_config || 0, oss_provider: user.oss_provider, oss_region: user.oss_region, oss_access_key_id: user.oss_access_key_id, // 安全修复:解密 OSS Access Key Secret(如果存在) oss_access_key_secret: user.oss_access_key_secret ? decryptSecret(user.oss_access_key_secret) : null, oss_bucket: user.oss_bucket, oss_endpoint: user.oss_endpoint, // 存储相关字段 storage_permission: user.storage_permission || 'oss_only', current_storage_type: user.current_storage_type || 'oss', local_storage_quota: user.local_storage_quota || 1073741824, local_storage_used: user.local_storage_used || 0, // 主题偏好 theme_preference: user.theme_preference || null }; next(); } catch (error) { if (error.name === 'TokenExpiredError') { return res.status(401).json({ success: false, message: '令牌已过期' }); } return res.status(401).json({ success: false, message: '无效的令牌' }); } } // 管理员权限中间件 function adminMiddleware(req, res, next) { if (!req.user || !req.user.is_admin) { return res.status(403).json({ success: false, message: '需要管理员权限' }); } next(); } /** * 管理员敏感操作二次验证中间件 * * 要求管理员重新输入密码才能执行敏感操作 * 防止会话劫持后的非法操作 * * @example * app.delete('/api/admin/users/:id', * authMiddleware, * adminMiddleware, * requirePasswordConfirmation, * async (req, res) => { ... } * ); */ function requirePasswordConfirmation(req, res, next) { const { password } = req.body; // 检查是否提供了密码 if (!password) { return res.status(400).json({ success: false, message: '执行此操作需要验证密码', require_password: true }); } // 验证密码长度(防止空密码) if (password.length < 6) { return res.status(400).json({ success: false, message: '密码格式错误' }); } // 从数据库重新获取用户信息(不依赖 req.user 中的数据) const user = UserDB.findById(req.user.id); if (!user) { return res.status(404).json({ success: false, message: '用户不存在' }); } // 验证密码 const isPasswordValid = UserDB.verifyPassword(password, user.password); if (!isPasswordValid) { // 记录安全日志:密码验证失败 SystemLogDB = require('./database').SystemLogDB; SystemLogDB.log({ level: SystemLogDB.LEVELS.WARN, category: SystemLogDB.CATEGORIES.SECURITY, action: 'admin_password_verification_failed', message: '管理员敏感操作密码验证失败', userId: req.user.id, username: req.user.username, ipAddress: req.ip, userAgent: req.get('user-agent'), details: { endpoint: req.path, method: req.method } }); return res.status(403).json({ success: false, message: '密码验证失败,操作已拒绝' }); } // 密码验证成功,继续执行 next(); } // 检查JWT密钥是否安全 function isJwtSecretSecure() { return !DEFAULT_SECRETS.includes(JWT_SECRET) && JWT_SECRET.length >= 32; } module.exports = { JWT_SECRET, generateToken, generateRefreshToken, refreshAccessToken, authMiddleware, adminMiddleware, requirePasswordConfirmation, // 导出二次验证中间件 isJwtSecretSecure, ACCESS_TOKEN_EXPIRES, REFRESH_TOKEN_EXPIRES };