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

MERN栈GET请求鉴权中间件无法读取Headers Token,Postman正常

问题根因

GET接口触发未授权错误的直接原因是前端Axios GET请求的参数顺序传错,导致鉴权Token没有被放到请求头中:

  • Axios的请求方法参数规则存在差异:POST/PUT等带请求体的方法,参数顺序为axios.方法名(请求地址, 请求体数据, 配置对象);但GET请求没有独立的请求体参数位,参数顺序为axios.get(请求地址, 配置对象)。
  • 你在shippingServices中编写的GET请求代码为axios.get(SHIPPING_ADDRESS_URL, userInfo, config),携带Token的config对象被错误放到了第三个参数位,Axios不会读取该位置的配置,最终发出的请求根本没有携带Authorization请求头,后端鉴权中间件拿不到Token自然返回401。
  • 这也完全匹配你遇到的现象:POST/PUT请求参数顺序正确、Token正常携带所以运行正常;Postman调试时手动添加了请求头所以不会报错。

除了这个核心触发点,你的前后端代码还存在几个会引发后续异常的隐藏bug,一并修复即可。

修复步骤

1. 前端请求逻辑修复

修改shippingServices.js中的GET请求方法,修正参数顺序,同时调整传参方式:

// get address
const get = async (userInfo) => {
    const config = {
        headers: {
            Authorization: `Bearer ${userInfo.token}`,
        },
        // GET请求不要传请求体,需要传递的参数放到params中,会自动拼接为URL查询参数
        params: {
            email: userInfo.email
        }
    };
    // 注意:GET请求第二个参数直接传config,去掉多余的userInfo参数位
    const { data } = await axios.get(SHIPPING_ADDRESS_URL, config);
    return data;
};

额外优化:ShippingPage.js中的useEffect依赖数组为空,首次加载时如果用户信息是异步拉取的,可能出现user为null导致读取token报错的问题,调整useEffect逻辑:

useEffect(() => {
    // 未登录直接跳登录页
    if (!user) {
        navigate("/login");
        return;
    }
    if (shippingAddress) {
        setAddress(shippingAddress.address);
        setCity(shippingAddress.city);
        setPostalCode(shippingAddress.postalCode);
        setCountry(shippingAddress.country);
    } else {
        const userInfo = {
            token: user.token,
            email: user.email,
        };
        dispatch(getAddress(userInfo));
    }
// 补全依赖项
}, [user, shippingAddress, navigate, dispatch]);

2. 后端逻辑修复

(1)优化鉴权中间件逻辑

原中间件存在冗余逻辑和流程漏洞,调整authMiddleware.js:

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

const protect = asyncHandler(async (req, res, next) => {
    let token;
    // 只从标准Authorization请求头读取Bearer Token,去掉无用的req.headers.token逻辑
    if (
        req.headers.authorization &&
        req.headers.authorization.startsWith("Bearer ")
    ) {
        try {
            token = req.headers.authorization.split(" ")[1];
            const decode = jwt.verify(token, process.env.JWT_SECRET);
            req.user = await User.findById(decode.id).select("-password");
            // 验证通过直接返回,终止后续逻辑
            return next();
        } catch (error) {
            res.status(401);
            throw new Error("未授权,Token无效");
        }
    }
    // 走到此处说明未获取到有效Token
    res.status(401);
    throw new Error("未授权,缺少访问Token");
});

export default protect;

(2)修复控制器逻辑漏洞

原控制器存在异步调用缺少await、GET请求读取不存在的req.body、字段拼写错误等问题,调整shippingAddressController.js中的对应方法:

  • 所有User.findById调用前补await,否则拿到的是pending状态的Promise,用户存在性判断完全失效(实际上鉴权中间件已经把用户信息挂到req.user上,不需要重复查询用户)
  • GET请求没有请求体,不要从req.body读取参数,从req.query读取URL参数,更稳妥的方式是直接通过req.user._id关联查询地址,完全不需要前端传递邮箱参数,避免用户篡改参数访问他人地址
  • 修复返回字段拼写错误:原代码中返回的county为拼写错误,和模型定义的country字段不匹配

修正后的getAddress方法参考:

// @desc   get address
// @route  GET /api/shippingaddress
// @access Private
const getAddress = asyncHandler(async (req, res) => {
    // 直接通过鉴权后挂载的req.user查询对应用户的地址,无需前端传邮箱
    const shippingAddress = await ShippingAddress.findOne({
        user: req.user._id,
    });

    if (shippingAddress) {
        res.status(200).json({
            address: shippingAddress.address,
            city: shippingAddress.city,
            postalCode: shippingAddress.postalCode,
            country: shippingAddress.country, // 修复原county拼写错误
            email: shippingAddress.userEmail,
        });
    } else {
        res.status(404);
        throw new Error("地址未找到");
    }
});

同步给saveAddress、changeAddress方法中的User.findById调用补上await关键字即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:33:23