发送无效Token时JsonWebTokenError未被中间件处理致应用崩溃
问题:无效JWT Token未被错误中间件处理导致应用崩溃
配置了错误处理中间件,但发送无效Token时,jwt.verify(request.token, process.env.SECRET)抛出JsonWebTokenError: invalid signature错误,该错误未被中间件捕获处理,导致应用崩溃。此问题出现在使用无效Token尝试添加新博客的场景中,涉及代码文件如下:
blog.js
const jwt = require("jsonwebtoken"); const blogsRouter = require("express").Router(); const Blog = require("../models/blog"); const User = require("../models/user"); blogsRouter.post("/", async (request, response) => { const body = request.body; const decodedToken = jwt.verify(request.token, process.env.SECRET); if (!decodedToken.id) { return response.status(401).json({ error: "token invalid" }); } const user = await User.findById(decodedToken.id); const newBlog = new Blog({ title: body.title, author: body.author, url: body.url, likes: body.likes || 0, user: user.id, }); const savedBlog = await newBlog.save(); user.blogs = user.blogs.concat(savedBlog._id); await user.save(); response.status(201).json(savedBlog); }); module.exports = blogsRouter
middleware.js
const unknownEndpoint = (request, response) => { response.status(404).send({ error: "unknown endpoint" }); }; const errorHandler = (error, request, response, next) => { logger.error(error.message); if (error.name === "CastError") { return response.status(400).send({ error: "malformatted id" }); } else if (error.name === "ValidationError") { return response.status(400).json({ error: error.message }); } else if (error.name === "JsonWebTokenError") { return response.status(401).json({ error: "invalid token" }); } else if (error.name === "TokenExpiredError") { return response.status(401).json({ error: "token expired", }); } next(error); }; const tokenExtractor = (request, response, next) => { const authorization = request.get("authorization"); if (authorization && authorization.startsWith("Bearer ")) { request.token = authorization.replace("Bearer ", ""); } next() } module.exports = { requestLogger, unknownEndpoint, errorHandler, tokenExtractor };
app.js
const express = require("express"); const app = express(); const cors = require("cors"); const mongoose = require("mongoose"); const config = require("./utils/config"); const logger = require("./utils/logger"); const middleware = require("./utils/middleware"); const blogsRouter = require("./controllers/blogs"); const usersRouter = require("./controllers/users"); const loginRouter = require("./controllers/login"); mongoose.set("strictQuery", false); mongoose .connect(config.MONGODB_URI) .then(() => { logger.info("connected to MongoDB"); }) .catch((error) => { logger.info("error connectig to MongoDB", error.message); }); app.use(cors()); app.use(express.json()); app.use(middleware.tokenExtractor) app.use("/api/blogs", blogsRouter); app.use("/api/users", usersRouter); app.use("/api/login", loginRouter); app.use(middleware.errorHandler); module.exports = app;
问题原因
Express的错误处理中间件仅能捕获两类错误:
- 同步代码中直接抛出的错误
- 通过
next(error)主动传递的错误
jwt.verify是同步方法,在async路由处理函数中抛出的错误会被封装为拒绝状态的Promise。若未显式捕获该Promise拒绝,Express无法自动将错误传递给错误处理中间件,最终导致应用崩溃。
解决方案
有两种可行的修复方式:
方式一:路由函数内用try/catch捕获错误
修改blog.js的POST路由逻辑,用try/catch包裹jwt.verify及后续异步操作,捕获错误后通过next(error)传递给错误处理中间件:
blogsRouter.post("/", async (request, response, next) => { try { const body = request.body; const decodedToken = jwt.verify(request.token, process.env.SECRET); if (!decodedToken.id) { return response.status(401).json({ error: "token invalid" }); } const user = await User.findById(decodedToken.id); const newBlog = new Blog({ title: body.title, author: body.author, url: body.url, likes: body.likes || 0, user: user.id, }); const savedBlog = await newBlog.save(); user.blogs = user.blogs.concat(savedBlog._id); await user.save(); response.status(201).json(savedBlog); } catch (error) { next(error); // 将错误传递给错误处理中间件 } });
方式二:新增Token验证中间件
将jwt.verify逻辑移至单独中间件,同步抛出的错误会被Express自动捕获并传递给错误处理中间件:
- 在
middleware.js中添加tokenValidator中间件:
const tokenValidator = (request, response, next) => { if (request.token) { const decodedToken = jwt.verify(request.token, process.env.SECRET); request.user = decodedToken; // 将解码后的Token存入request对象 } next(); };
更新导出内容:
module.exports = { requestLogger, unknownEndpoint, errorHandler, tokenExtractor, tokenValidator };
- 在
app.js中,于tokenExtractor之后、路由挂载前添加该中间件:
app.use(middleware.tokenExtractor); app.use(middleware.tokenValidator); // 新增此行 app.use("/api/blogs", blogsRouter); // ... 其他路由
- 修改
blog.js的路由函数,直接使用request中存储的解码信息:
blogsRouter.post("/", async (request, response) => { const body = request.body; if (!request.user?.id) { return response.status(401).json({ error: "token invalid" }); } const user = await User.findById(request.user.id); // ... 后续逻辑保持不变 });
两种方式均可让JsonWebTokenError被错误处理中间件捕获,返回正确的401响应,避免应用崩溃。
内容的提问来源于stack exchange,提问作者Vedagya Jha
相关产品推荐
相关产品推荐

