You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS中JWT路由保护失效问题求助

NextJS JWT验证中间件失效:POST请求可绕过验证插入数据

我正在开发一个NextJS项目,使用JWT保护部分路由,但JWT验证中间件失效——index.js中的POST请求可绕过验证向数据库插入数据。已确认验证中间件已触发,但请求数据仍被插入数据库。

问题代码

verifyAuthToken.middleware.js

import jwt from "jsonwebtoken";

export default function verifyAuthToken(req, res) {
  try {
    const token = req.headers;
    if (!token) {
      return res.status(401).json({
        success: false,
        msg: "You are not authorized to access this route",
      });
    }

    const { userId } = jwt.verify(
      req.headers.authorization,
      process.env.TOKEN_SECRET
    );
    if (!userId) {
      return res.status(401).json({
        success: false,
        msg: "You are not authorized to access this route",
      });
    }
    req.userId = userId;
  } catch (error) {
    res.status(401).json({
      success: false,
      msg: "You are not authorized to access this route",
      error: error.message,
    });
  }
}

index.js(API路由)

import verifyAuthToken from "../../../middleware/verifyAuthToken.middleware";
import Evoluter from "../../../models/Evoluter.model";
import dbConnect from "../../../server-utils/connectDB";
import { sendEmail } from "../../../server-utils/sendEmail";
import baseURL from "../../../utils/baseURL";

export default async (req, res) => {
  const { method } = req;
  verifyAuthToken(req, res);
  await dbConnect();

  switch (method) {
    // @route   GET api/evoluters
    // @desc    Create new evoluter
    // @access  Private
    case "POST":
      try {
        const { name, email } = req.body;

        const evoluter = await Evoluter.findOne({ email: email.toLowerCase() });
        if (evoluter) {
          return res.status(400).json({
            success: false,
            msg: "Evoluter already exists",
          });
        }

        const newEvoluter = await Evoluter.create({
          name,
          email: email.toLowerCase(),
        });

        try {
          await sendEmail({
            email: newEvoluter.email,
            subject: "Welcome to Rubrica",
            message: `Hi ${newEvoluter.name}, welcome to Rubrica. Please click on the link below to activate your account. ${baseURL}/home/${newEvoluter._id}`,
          });

          res.status(201).json({
            success: true,
            data: newEvoluter,
            msg: "Evoluter created successfully. Please check your email to activate your account.",
          });
        } catch (error) {
          console.log(error);
          res.status(500).json({
            success: false,
            msg: "Error sending invitation email, please let the admin know.",
          });
        }
      } catch (error) {
        res.status(400).json({
          success: false,
          msg: "Sorry, we couldn't send the invitation. Please try again.",
          error: error,
        });
      }
      break;

    default:
      res.status(400).json({
        success: false,
        msg: "Invalid request",
      });
      break;
  }
};

问题原因

  1. Token获取逻辑错误:中间件中把req.headers赋值给token,然后判断!token——req.headers永远存在,这个判断完全无效。正确的做法应该检查req.headers.authorization是否存在,并且处理Bearer <token>的格式。

  2. 未终止后续代码执行:即使中间件返回了401响应,API路由中的await dbConnect()和POST请求逻辑依然会执行。中间件的return res.status(401)...只是结束中间件函数本身,不会阻止API路由里的后续代码运行。

修复方案

方案1:使用NextJS中间件的next()模式

修正后的verifyAuthToken.middleware.js

import jwt from "jsonwebtoken";

// 添加next参数,验证通过后调用next()放行
export default function verifyAuthToken(req, res, next) {
  try {
    const authHeader = req.headers.authorization;
    // 检查Authorization头是否存在,且符合Bearer格式
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return res.status(401).json({
        success: false,
        msg: "You are not authorized to access this route",
      });
    }

    // 提取token部分
    const token = authHeader.split(' ')[1];
    const { userId } = jwt.verify(token, process.env.TOKEN_SECRET);
    
    if (!userId) {
      return res.status(401).json({
        success: false,
        msg: "You are not authorized to access this route",
      });
    }
    
    req.userId = userId;
    // 验证通过,执行后续代码
    next();
  } catch (error) {
    res.status(401).json({
      success: false,
      msg: "You are not authorized to access this route",
      error: error.message,
    });
  }
}

修正后的index.js

import verifyAuthToken from "../../../middleware/verifyAuthToken.middleware";
import Evoluter from "../../../models/Evoluter.model";
import dbConnect from "../../../server-utils/connectDB";
import { sendEmail } from "../../../server-utils/sendEmail";
import baseURL from "../../../utils/baseURL";

export default async (req, res) => {
  const { method } = req;
  
  // 用Promise包装中间件,确保验证不通过时终止后续代码
  await new Promise((resolve) => {
    verifyAuthToken(req, res, resolve);
  });

  // 检查是否已发送响应,避免重复执行逻辑
  if (res.headersSent) {
    return;
  }

  await dbConnect();

  switch (method) {
    case "POST":
      try {
        const { name, email } = req.body;

        const evoluter = await Evoluter.findOne({ email: email.toLowerCase() });
        if (evoluter) {
          return res.status(400).json({
            success: false,
            msg: "Evoluter already exists",
          });
        }

        const newEvoluter = await Evoluter.create({
          name,
          email: email.toLowerCase(),
        });

        try {
          await sendEmail({
            email: newEvoluter.email,
            subject: "Welcome to Rubrica",
            message: `Hi ${newEvoluter.name}, welcome to Rubrica. Please click on the link below to activate your account. ${baseURL}/home/${newEvoluter._id}`,
          });

          res.status(201).json({
            success: true,
            data: newEvoluter,
            msg: "Evoluter created successfully. Please check your email to activate your account.",
          });
        } catch (error) {
          console.log(error);
          res.status(500).json({
            success: false,
            msg: "Error sending invitation email, please let the admin know.",
          });
        }
      } catch (error) {
        res.status(400).json({
          success: false,
          msg: "Sorry, we couldn't send the invitation. Please try again.",
          error: error,
        });
      }
      break;

    default:
      res.status(400).json({
        success: false,
        msg: "Invalid request",
      });
      break;
  }
};

方案2:用Promise包装中间件(更简洁)

修正后的verifyAuthToken.middleware.js

import jwt from "jsonwebtoken";

// 返回Promise,方便API路由中await处理
export default function verifyAuthToken(req, res) {
  return new Promise((resolve, reject) => {
    try {
      const authHeader = req.headers.authorization;
      if (!authHeader || !authHeader.startsWith('Bearer ')) {
        res.status(401).json({
          success: false,
          msg: "You are not authorized to access this route",
        });
        return reject(new Error("Authorization header missing or invalid"));
      }

      const token = authHeader.split(' ')[1];
      const { userId } = jwt.verify(token, process.env.TOKEN_SECRET);
      
      if (!userId) {
        res.status(401).json({
          success: false,
          msg: "You are not authorized to access this route",
        });
        return reject(new Error("Invalid user ID in token"));
      }
      
      req.userId = userId;
      resolve();
    } catch (error) {
      res.status(401).json({
        success: false,
        msg: "You are not authorized to access this route",
        error: error.message,
      });
      reject(error);
    }
  });
}

修正后的index.js

import verifyAuthToken from "../../../middleware/verifyAuthToken.middleware";
import Evoluter from "../../../models/Evoluter.model";
import dbConnect from "../../../server-utils/connectDB";
import { sendEmail } from "../../../server-utils/sendEmail";
import baseURL from "../../../utils/baseURL";

export default async (req, res) => {
  const { method } = req;
  
  try {
    // 等待验证完成,验证失败会进入catch
    await verifyAuthToken(req, res);
  } catch (error) {
    // 验证失败,已返回响应,直接终止
    return;
  }

  // 双重检查避免重复执行
  if (res.headersSent) return;

  await dbConnect();

  switch (method) {
    case "POST":
      try {
        const { name, email } = req.body;

        const evoluter = await Evoluter.findOne({ email: email.toLowerCase() });
        if (evoluter) {
          return res.status(400).json({
            success: false,
            msg: "Evoluter already exists",
          });
        }

        const newEvoluter = await Evoluter.create({
          name,
          email: email.toLowerCase(),
        });

        try {
          await sendEmail({
            email: newEvoluter.email,
            subject: "Welcome to Rubrica",
            message: `Hi ${newEvoluter.name}, welcome to Rubrica. Please click on the link below to activate your account. ${baseURL}/home/${newEvoluter._id}`,
          });

          res.status(201).json({
            success: true,
            data: newEvoluter,
            msg: "Evoluter created successfully. Please check your email to activate your account.",
          });
        } catch (error) {
          console.log(error);
          res.status(500).json({
            success: false,
            msg: "Error sending invitation email, please let the admin know.",
          });
        }
      } catch (error) {
        res.status(400).json({
          success: false,
          msg: "Sorry, we couldn't send the invitation. Please try again.",
          error: error,
        });
      }
      break;

    default:
      res.status(400).json({
        success: false,
        msg: "Invalid request",
      });
      break;
  }
};

关键修复点总结

  • 修正Token提取逻辑:正确处理Authorization: Bearer <token>的格式
  • 控制代码流:确保验证失败时,后续的数据库操作和业务逻辑不会执行
  • 增加格式校验:防止无意义的token格式绕过验证

内容的提问来源于stack exchange,提问作者Manikangkan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 20:31:34