2026-07-27 11:28:33 +08:00

164 lines
5.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const jwt = require("jsonwebtoken");
const logger = require("../utils/logger");
const { sendError } = require("../utils/response");
const tokenStore = require("./useTokenStore");
const {
isTypeInWhitelist,
isPathInWhitelist,
} = require("../config/auth-whitelist");
/**
* 智能鉴权中间件
* 根据请求的 type 参数或路径判断是否需要进行 JWT 验证
* 如果 type 在白名单中或路径在白名单中,则跳过 JWT 验证
*/
const smartAuth = (req, res, next) => {
try {
const requestPath = req.path;
// 检查路径是否在白名单中
if (isPathInWhitelist(requestPath)) {
console.log(`路径 ${requestPath} 在白名单中,跳过鉴权`);
return next();
}
// 从请求参数中获取 type支持 body 和 query
const type = req.body?.type || req.query?.type;
// 检查 type 是否在白名单中
if (type && isTypeInWhitelist(type)) {
console.log(`请求类型 ${type} 在白名单中,跳过鉴权`);
return next();
}
// 不在白名单中,执行正常的 JWT 验证
const authHeader = req.headers.authorization;
if (!authHeader) {
logger.warn(`未授权访问: ${requestPath}, type: ${type}`);
return sendError(res, "未提供认证令牌", "No token provided", 401);
}
// 支持两种格式: "Bearer token" 或 "token"
const token = authHeader.startsWith("Bearer ")
? authHeader.substring(7)
: authHeader;
if (!token) {
return sendError(res, "令牌格式错误", "Invalid token format", 401);
}
const secret = process.env.JWT_SECRET || "your-secret-key";
jwt.verify(token, secret, async (err, decoded) => {
if (err) {
if (err.name === "TokenExpiredError") {
logger.warn(`令牌已过期: ${requestPath}`);
return sendError(res, "令牌已过期", "Token expired", 401);
}
if (err.name === "JsonWebTokenError") {
logger.warn(`无效的令牌: ${requestPath}`);
return sendError(res, "无效的令牌", "Invalid token", 401);
}
logger.error("Token验证失败:", err);
return sendError(res, "令牌验证失败", "Token verification failed", 401);
}
if (decoded.platform === 'PC') {
const storedToken = await tokenStore.getToken(decoded.userId);
if (storedToken !== token) {
logger.warn(`令牌不匹配: ${requestPath}`);
return sendError(res, "令牌不匹配", "Token does not match", 401);
}
}
// 将解码后的用户信息添加到请求对象
req.user = decoded;
console.log(`用户 ${decoded.username || decoded.userId} 鉴权成功`);
next();
});
} catch (error) {
logger.error("smartAuth中间件错误:", error);
return sendError(res, "认证处理失败", error.message, 500);
}
};
/**
* 创建自定义智能鉴权中间件
* 允许在路由级别指定额外的白名单 types
* @param {string[]} additionalWhitelist - 额外的白名单 types
* @returns {Function} 中间件函数
*/
const createSmartAuth = (additionalWhitelist = []) => {
return (req, res, next) => {
try {
const requestPath = req.path;
// 检查路径是否在白名单中
if (isPathInWhitelist(requestPath)) {
console.log(`路径 ${requestPath} 在白名单中,跳过鉴权`);
return next();
}
// 从请求参数中获取 type
const type = req.body?.type || req.query?.type;
// 检查 type 是否在全局白名单或自定义白名单中
if (
type &&
(isTypeInWhitelist(type) || additionalWhitelist.includes(type))
) {
console.log(`请求类型 ${type} 在白名单中,跳过鉴权`);
return next();
}
// 执行 JWT 验证(与 smartAuth 相同的逻辑)
const authHeader = req.headers.authorization;
if (!authHeader) {
logger.warn(`未授权访问: ${requestPath}, type: ${type}`);
return sendError(res, "未提供认证令牌", "No token provided", 401);
}
const token = authHeader.startsWith("Bearer ")
? authHeader.substring(7)
: authHeader;
if (!token) {
return sendError(res, "令牌格式错误", "Invalid token format", 401);
}
const secret = process.env.JWT_SECRET || "your-secret-key";
jwt.verify(token, secret, (err, decoded) => {
if (err) {
if (err.name === "TokenExpiredError") {
logger.warn(`令牌已过期: ${requestPath}`);
return sendError(res, "令牌已过期", "Token expired", 401);
}
if (err.name === "JsonWebTokenError") {
logger.warn(`无效的令牌: ${requestPath}`);
return sendError(res, "无效的令牌", "Invalid token", 401);
}
logger.error("Token验证失败:", err);
return sendError(
res,
"令牌验证失败",
"Token verification failed",
401
);
}
req.user = decoded;
console.log(`用户 ${decoded.username || decoded.userId} 鉴权成功`);
next();
});
} catch (error) {
logger.error("createSmartAuth中间件错误:", error);
return sendError(res, "认证处理失败", error.message, 500);
}
};
};
module.exports = {
smartAuth,
createSmartAuth,
};