hn-hlw-service/middleware/smart-auth.js

164 lines
5.3 KiB
JavaScript
Raw Normal View History

2026-07-27 11:28:33 +08:00
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,
};