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

MERN栈登录后无法访问受保护路由:提示无授权无Token

问题排查与修复方案

根据你提供的代码,出现「Not Authorized, No token」错误的核心原因大概率是请求未正确携带JWT令牌,结合代码细节和场景,以下是具体排查点和修复方案:

1. 最可能的原因:登录后未及时更新Axios全局请求头

你的App.js中,useEffect仅在组件初始挂载时读取localStorage设置Axios头,但用户登录操作是在Login组件完成的——此时App已经挂载完毕,useEffect不会重新执行,导致新存入localStorage的token没有同步到Axios的全局配置中。

修复方案:

在Login组件的登录成功回调中,主动设置Axios全局头,而不是仅依赖App的初始挂载逻辑:

// Login组件内的登录成功处理逻辑示例
const handleLogin = async (credentials) => {
  try {
    const res = await axios.post("/api/users/login", credentials);
    const { token } = res.data;
    // 存入localStorage
    const tokenObj = { token };
    localStorage.setItem("authStatus", JSON.stringify(tokenObj));
    // 立即设置Axios全局头
    axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
    // 更新Redux状态并跳转
    const { username } = jwt_decode(token);
    dispatch(updateName(username));
    navigate("/tickets");
  } catch (err) {
    // 处理登录错误
  }
};

2. 强化App.js中Axios头设置的鲁棒性

当前useEffect未处理localStorage内容损坏、token为空的情况,可能导致设置无效的Bearer undefined头,后端无法识别。

修复方案:

更新App.js的useEffect逻辑,增加异常处理和空值判断:

useEffect(() => {
  const authStatusStr = localStorage.getItem("authStatus");
  if (authStatusStr) {
    try {
      const tokenObj = JSON.parse(authStatusStr);
      // 确保token存在且有效
      if (tokenObj?.token) {
        axios.defaults.headers.common["Authorization"] = `Bearer ${tokenObj.token}`;
        const decoded = jwt_decode(tokenObj.token);
        dispatch(updateName(decoded.username));
      } else {
        // token为空时清除无效头
        delete axios.defaults.headers.common["Authorization"];
        localStorage.removeItem("authStatus");
      }
    } catch (err) {
      // JSON解析失败,清除损坏的存储内容
      localStorage.removeItem("authStatus");
      delete axios.defaults.headers.common["Authorization"];
    }
  } else {
    // 无登录状态时清除Axios头
    delete axios.defaults.headers.common["Authorization"];
  }
}, [dispatch]);

3. 修复后端中间件的逻辑漏洞

当前authMiddleware.js中,若jwt.verify抛出错误(如token过期、无效),会执行next(err),但后续仍可能走到if (!token)的判断(虽然此时token已有值,但代码逻辑存在冗余),且错误提示不明确。

修复方案:

优化中间件逻辑,提前返回并明确错误类型:

const requireAuth = asyncHandler(async (req, res, next) => {
  let token;
  const authHeader = req.headers.authorization;

  if (authHeader && authHeader.startsWith("Bearer")) {
    token = authHeader.split(" ")[1];
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      // 避免返回用户密码
      req.user = await User.findById(decoded.id).select("-password");
      next();
      return; // 处理完成后直接返回,跳过后续逻辑
    } catch (err) {
      // 明确返回无效token错误
      return next(createError(401, "Not Authorized, Invalid or Expired Token"));
    }
  }

  // 走到此处说明无有效token
  next(createError(401, "Not Authorized. No Token"));
});

4. 排查其他潜在问题

  • 检查请求头是否被覆盖:确认所有请求(如Tickets组件中的GET请求)没有单独设置headers覆盖全局的Authorization头,若需要自定义头,需合并全局配置:
    axios.get("/api/users/tickets", {
      headers: {
        ...axios.defaults.headers.common,
        // 其他自定义头
      }
    });
    
  • 验证localStorage结构:在浏览器控制台执行localStorage.getItem("authStatus"),确认返回的是包含token字段的JSON对象,而非纯字符串或损坏的JSON。
  • 检查token有效期:若token已过期,后端会抛出无效token错误,需确保登录时返回的token有效期符合预期,或在前端添加token过期检测逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 11:27:28