Files
yunrui_asset/backend/middleware/auth.js
T
zhang1106 63f0cb570e refactor: 统一代码风格并迁移至 ESLint 新配置
style(backend): 格式化模型文件代码
style(frontend): 调整组件代码格式
chore: 删除旧 ESLint 配置并添加新配置
refactor(backend): 重构模型定义语法
style: 统一箭头函数和对象属性简写
2026-03-27 19:12:16 +08:00

246 lines
6.0 KiB
JavaScript

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 {
let token = null;
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
token = authHeader.substring(7);
}
if (!token && req.query.token) {
token = req.query.token;
}
if (!token) {
return res.status(401).json({
success: false,
message: '未提供认证令牌',
});
}
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,
};