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

React+Axios调用后端API遭遇401 Unauthorized问题求助

问题分析:Axios携带JWT令牌仍返回401 Unauthorized

在React应用中调用后端创建店铺的API时,持续收到401 Unauthorized错误。用户登录后已将JWT令牌存入localStorage,发起multipart/form-data类型POST请求时,已在Axios请求头中设置Authorization: Bearer ${token},但认证始终失败。


前端代码

const submitAddStore = async (event) => {
    event.preventDefault();
    const formData = new FormData();
    formData.append("pic", pic);
    formData.append("title", title);
    formData.append("tele", tele);
    formData.append("desc", desc);
    formData.append("location", location);

    try {
      if (!title || !tele || !desc || !pic || !location) {
        Swal.fire({
          position: "center",
          icon: "warning",
          title: "عذرا",
          text: "المرجو ملء جميع الحقول   ",
        });
        return;
      }
      const token = localStorage.getItem("token"); // 从localStorage获取令牌

      const result = await axios.post(
        "http://localhost:5000/api/create-store",
        formData,
        {
          headers: {
            "content-type": "multipart/form-data",
            Authorization: `Bearer ${token}`,
          },
        }
      );

后端代码

app.post(
  "/api/create-store",
  protect,
  upload2.single("pic"),
  async (req, res) => {
    const { title, desc, tele, location } = req.body;
    const picName = req.file.filename;

    try {
      const newStore = new store({
        title,
        desc,
        tele,
        location,
        pic: picName,
        owner: req.user._id,
      });
      await newStore.save();
      res.json({ status: "ok" });
    } catch (error) {
      console.error("Error creating store:", error);
      res.json({ status: "error" });
    }
  }
);

认证中间件代码

import jwt from "jsonwebtoken";
import asyncHandler from "express-async-handler";
import User from "../models/useModel.js";

const protect = asyncHandler(async (req, res, next) => {
  let token;

  token = req.cookies.jwt;

  if (token) {
    console.log("Received Token:", token);

    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      console.log("Decoded User ID:", decoded.userId);

      req.user = await User.findById(decoded.userId).select("-password");
      console.log("User from Database:", req.user);

      next();
    } catch (error) {
      console.error(error);
      res.status(401);
      throw new Error("Not authorized, token failed");
    }
  } else {
    res.status(401);
    throw new Error("Not authorized, no token");
  }
});

export default protect;

核心原因

后端protect中间件仅从Cookie的jwt字段读取令牌,完全没有处理前端通过Authorization请求头发送的Bearer令牌,导致前端传递的令牌根本没被后端识别,直接触发"无令牌"的401错误。

修复方案

1. 修改后端中间件,支持从Authorization头读取令牌

更新认证逻辑,同时兼容Cookie和请求头两种令牌传递方式:

import jwt from "jsonwebtoken";
import asyncHandler from "express-async-handler";
import User from "../models/useModel.js";

const protect = asyncHandler(async (req, res, next) => {
  let token;

  // 优先处理Authorization头中的Bearer令牌
  if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
    try {
      token = req.headers.authorization.split(' ')[1]; // 提取Bearer后的令牌部分
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      req.user = await User.findById(decoded.userId).select("-password");
      next();
      return;
    } catch (error) {
      console.error(error);
      res.status(401);
      throw new Error("Not authorized, token failed");
    }
  }

  // 兼容原有Cookie读取逻辑
  token = req.cookies.jwt;
  if (token) {
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      req.user = await User.findById(decoded.userId).select("-password");
      next();
    } catch (error) {
      console.error(error);
      res.status(401);
      throw new Error("Not authorized, token failed");
    }
  } else {
    res.status(401);
    throw new Error("Not authorized, no token");
  }
});

export default protect;

2. 前端代码优化(可选)

  • 手动设置content-type: multipart/form-data会丢失浏览器自动生成的boundary标识,导致后端无法正确解析表单数据,建议移除该配置:
const result = await axios.post(
  "http://localhost:5000/api/create-store",
  formData,
  {
    headers: {
      Authorization: `Bearer ${token}`, // 仅保留认证头
    },
  }
);
  • 可以在请求前打印token值,确认localStorage.getItem("token")能正确获取到有效令牌。

额外排查点

  • 用jwt.io解码令牌,检查exp字段确认令牌是否过期
  • 确认后端process.env.JWT_SECRET和登录时生成令牌使用的密钥完全一致
  • 跨域场景下,确保后端CORS配置允许携带Authorization请求头

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 03:08:22