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
相关产品推荐
相关产品推荐

