164 lines
5.3 KiB
JavaScript
164 lines
5.3 KiB
JavaScript
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,
|
||
};
|