Files
niumalll/backend/middleware/auth.js
2025-09-04 09:04:58 +08:00

184 lines
4.2 KiB
JavaScript

const jwt = require('jsonwebtoken');
const User = require('../models/User');
// JWT认证中间件
const authenticateToken = async (req, res, next) => {
try {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失',
code: 'TOKEN_MISSING'
});
}
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// 查找用户
const user = await User.findByPk(decoded.userId, {
attributes: { exclude: ['password_hash'] }
});
if (!user) {
return res.status(401).json({
success: false,
message: '用户不存在',
code: 'USER_NOT_FOUND'
});
}
if (user.status !== 'active') {
return res.status(401).json({
success: false,
message: '用户账号已被禁用',
code: 'USER_DISABLED'
});
}
// 将用户信息附加到请求对象
req.user = user;
next();
} catch (error) {
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({
success: false,
message: '无效的访问令牌',
code: 'INVALID_TOKEN'
});
} else if (error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌已过期',
code: 'TOKEN_EXPIRED'
});
}
return res.status(500).json({
success: false,
message: '认证服务错误',
error: error.message
});
}
};
// 权限检查中间件
const requireRole = (roles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
success: false,
message: '用户未认证',
code: 'USER_NOT_AUTHENTICATED'
});
}
const userRoles = Array.isArray(roles) ? roles : [roles];
if (!userRoles.includes(req.user.user_type)) {
return res.status(403).json({
success: false,
message: '权限不足',
code: 'INSUFFICIENT_PERMISSIONS',
requiredRoles: userRoles,
userRole: req.user.user_type
});
}
next();
};
};
// 生成JWT token
const generateToken = (user) => {
const payload = {
userId: user.id,
username: user.username,
userType: user.user_type
};
return {
accessToken: jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
}),
refreshToken: jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET + '_refresh',
{ expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d' }
)
};
};
// 刷新token
const refreshToken = async (req, res, next) => {
try {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(400).json({
success: false,
message: '刷新令牌缺失'
});
}
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET + '_refresh');
const user = await User.findByPk(decoded.userId, {
attributes: { exclude: ['password_hash'] }
});
if (!user || user.status !== 'active') {
return res.status(401).json({
success: false,
message: '无效的刷新令牌'
});
}
const tokens = generateToken(user);
res.json({
success: true,
message: '令牌刷新成功',
data: tokens
});
} catch (error) {
return res.status(401).json({
success: false,
message: '刷新令牌无效或已过期'
});
}
};
// 可选认证中间件(不强制要求登录)
const optionalAuth = async (req, res, next) => {
try {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (token) {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findByPk(decoded.userId, {
attributes: { exclude: ['password_hash'] }
});
if (user && user.status === 'active') {
req.user = user;
}
}
next();
} catch (error) {
// 忽略错误,继续请求
next();
}
};
module.exports = {
authenticateToken,
requireRole,
generateToken,
refreshToken,
optionalAuth
};