const jwt = require('jsonwebtoken'); const User = require('../models/User'); function getJwtSecret() { const envSecret = process.env.JWT_SECRET; if (process.env.NODE_ENV === 'production') { if (!envSecret) { throw new Error( '[致命错误] 生产环境未设置 JWT_SECRET 环境变量!\n' + '请在服务器环境变量中设置强密钥(至少32位随机字符)。\n' + '生成命令(PowerShell):-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | ForEach-Object { [char]$_ })' ); } if (envSecret.length < 32) { throw new Error('[致命错误] 生产环境 JWT_SECRET 长度必须至少32位!当前长度:' + envSecret.length); } return envSecret; } if (!envSecret) { throw new Error( '[错误] JWT_SECRET 未配置,请检查 initConfig.js 是否正确执行' ); } return envSecret; } const JWT_SECRET = getJwtSecret(); const TOKEN_EXPIRY = process.env.TOKEN_EXPIRY || '24h'; const getBrowserInfo = (userAgent) => { let device = 'Desktop'; let browser = 'Unknown'; let os = 'Unknown'; if (/Mobile|Android|iPhone|iPad|iPod/i.test(userAgent)) { device = 'Mobile'; if (/iPad/i.test(userAgent)) device = 'Tablet'; } if (/Firefox/i.test(userAgent)) { browser = 'Firefox'; } else if (/Chrome/i.test(userAgent)) { browser = 'Chrome'; } else if (/Safari/i.test(userAgent)) { browser = 'Safari'; } else if (/Edge/i.test(userAgent)) { browser = 'Edge'; } else if (/MSIE|Trident/i.test(userAgent)) { browser = 'IE'; } if (/Windows/i.test(userAgent)) { os = 'Windows'; } else if (/Mac OS/i.test(userAgent)) { os = 'macOS'; } else if (/Linux/i.test(userAgent)) { os = 'Linux'; } else if (/Android/i.test(userAgent)) { os = 'Android'; } else if (/iOS|iPhone|iPad|iPod/i.test(userAgent)) { os = 'iOS'; } return { device, browser, os }; }; const generateToken = (user) => { return jwt.sign( { userId: user.userId, username: user.username, roleId: user.roleId }, JWT_SECRET, { expiresIn: TOKEN_EXPIRY } ); }; const verifyToken = (token) => { try { return jwt.verify(token, JWT_SECRET); } catch (error) { return null; } }; const authMiddleware = async (req, res, next) => { try { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ success: false, message: '未提供认证令牌' }); } const token = authHeader.substring(7); const decoded = verifyToken(token); if (!decoded) { return res.status(401).json({ success: false, message: '令牌无效或已过期' }); } // 验证用户是否存在,增加详细的错误日志 let user; try { user = await User.findByPk(decoded.userId); } catch (dbError) { // 数据库查询错误 console.error('[认证中间件] 数据库查询失败:', { userId: decoded.userId, error: dbError.message, stack: dbError.stack, timestamp: new Date().toISOString() }); return res.status(500).json({ success: false, message: '数据库查询失败,请稍后重试' }); } if (!user) { console.warn('[认证中间件] 用户不存在:', { userId: decoded.userId, username: decoded.username, timestamp: new Date().toISOString() }); return res.status(401).json({ success: false, message: '用户不存在' }); } if (user.status === 'locked') { console.warn('[认证中间件] 账户已被锁定:', { userId: user.userId, username: user.username, timestamp: new Date().toISOString() }); return res.status(403).json({ success: false, message: '账户已被锁定' }); } if (user.status === 'inactive') { console.warn('[认证中间件] 账户已禁用:', { userId: user.userId, username: user.username, timestamp: new Date().toISOString() }); return res.status(403).json({ success: false, message: '账户已禁用' }); } req.user = decoded; req.userModel = user; next(); } catch (error) { // 捕获未预期的错误 console.error('[认证中间件] 未预期的错误:', { error: error.message, stack: error.stack, url: req?.url, method: req?.method, ip: req?.ip, timestamp: new Date().toISOString() }); return res.status(500).json({ success: false, message: '认证失败,请稍后重试' }); } }; const optionalAuth = async (req, res, next) => { try { const authHeader = req.headers.authorization; if (authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.substring(7); const decoded = verifyToken(token); if (decoded) { let user; try { user = await User.findByPk(decoded.userId); } catch (dbError) { // 数据库错误时不中断请求,仅记录日志 console.warn('[可选认证] 数据库查询失败:', { userId: decoded.userId, error: dbError.message, timestamp: new Date().toISOString() }); // 继续执行,不设置用户信息 next(); return; } if (user && user.status === 'active') { req.user = decoded; req.userModel = user; } } } next(); } catch (error) { // 可选认证失败不影响主流程,仅记录日志 console.warn('[可选认证] 认证失败(已忽略):', { error: error.message, url: req?.url, timestamp: new Date().toISOString() }); next(); } }; module.exports = { generateToken, verifyToken, authMiddleware, optionalAuth, JWT_SECRET, TOKEN_EXPIRY };