Node.js Express中如何在中间件内调用其他中间件
问题背景
使用Node.js + Express搭配JWT认证开发时,遇到中间件使用问题:部分公开路由不需要强制用户登录,但如果用户处于已登录状态,需要解析用户身份并挂载到req.auth属性,供后续业务逻辑使用。
作为Express初学者,自行编写了isLoggedIn中间件,逻辑为检测客户端是否携带token:无token则直接放行,检测到token时尝试调用authorize授权中间件完成身份解析。但实际运行时授权逻辑被直接跳过,通过console.log打印执行链路调试始终未定位问题。
原有问题实现代码
isLoggedIn中间件实现:
function isLoggedIn() { return (req, res, next) => { var clientToken // 检测客户端是否携带token,无token则返回null if (req.headers.authorization && req.headers.authorization.split(" ")[0] === "Bearer") { clientToken = req.headers.authorization.split(" ")[1]; } else if (req.query && req.query.token) { clientToken = req.query.token; } else if (req.cookies && req.cookies['session']) { clientToken = req.cookies['session']; } else { clientToken = null; } if (!clientToken) next(); else authorize(); } }
authorize授权中间件实现:
function authorize(roles = []) { console.log("1"); // roles参数支持传入单个角色字符串(如 Role.User 或 'User') // 也支持传入角色数组(如 [Role.Admin, Role.User] 或 ['Admin', 'User']) if (typeof roles === 'string') { roles = [roles]; } console.log("2"); return [ // 校验JWT token,将解析出的用户信息挂载到请求对象的req.auth属性 jwt({ secret, algorithms: ['HS256'], getToken: function fromHeaderOrQuerystring(req) { console.log("4"); if (req.headers.authorization && req.headers.authorization.split(" ")[0] === "Bearer") { console.log("why is it here?"); return req.headers.authorization.split(" ")[1]; } else if (req.query && req.query.token) { console.log("query string?") return req.query.token; } else if (req.cookies && req.cookies['session']) { console.log("5"); return req.cookies['session']; } console.log("null?"); return null; } }), // 基于用户角色做权限校验 async(req, res, next) => { // 这部分逻辑始终没有执行 console.log("7"); const account = await User.findOne({ id: req.auth.sub }); const refreshTokens = await refreshToken.find({ account: account.id }); if (!account || (roles.length && !roles.includes(account.role))) { // 账号不存在或角色无权限 return res.status(401).json({ message: 'Unauthorized' }); } // 认证授权通过 req.auth = account; req.auth.ownsToken = token => !!refreshTokens.find(x => x.token === token); next(); } ]; }
原有路由使用逻辑
- 公开路由:
app.get("/", isLoggedIn(), (req, res) => res.render('index'));,预期效果为未登录用户可正常访问,登录用户可通过req.auth获取身份信息 - 受保护路由:
app.get("/user", authorize(), (req, res) => res.render('user'));,预期效果为未登录用户无法访问
最终解决方案
调整中间件设计思路,移除单独编写的isLoggedIn中间件,通过Express标准中间件组合实现需求:
- 受保护路由直接挂载
authorize()中间件,强制要求登录鉴权 - 公开路由按顺序挂载
authorize()、新增的NoLoginRequired错误处理中间件,实现可选登录解析 - 后续可优化
authorize()实现,使其符合Express官方中间件规范,不再返回函数数组
最终路由示例
- 公开路由:
app.get("/unprotected", authorize.authorize(), authorize.NoLoginRequired, (req, res) => res.render('unprotectedview')); - 受保护路由:
app.get("/user", authorize.authorize(), (req, res) => res.render('user'));
NoLoginRequired中间件实现
注意:Express错误处理中间件必须保持(err, req, res, next)的四参数签名,挂载时不要加括号执行
function NoLoginRequired(err, req, res, next) { if (err && err.name === "UnauthorizedError") { // 捕获JWT校验抛出的未授权错误,直接放行,不要求用户必须登录 next(); } else { // 其余错误正常传递给后续错误处理逻辑 next(err); } }
该方案符合Express中间件设计规范,可稳定实现公开路由可选登录态解析、受保护路由强制鉴权的需求。
内容的提问来源于stack exchange,提问作者Waaaaaa
相关产品推荐
相关产品推荐

