前端携带Token请求后端接口出现TypeError,请求协助排查
JWT身份认证客户端请求失败排查
我实现了基于JWT的身份认证功能:前端登录后从后端获取Token并存入localStorage,点击提交按钮时携带Token请求受保护的/api/v1/inventories接口。用Postman测试所有接口都正常,但客户端发送请求时后端报错,无法定位问题,求协助。
Frontend Code
<script> const formDOM = document.querySelector(".form"); const btnSubmitDOM = document.querySelector(".btn-submit"); // get token form server formDOM.addEventListener("submit", async (e) => { e.preventDefault(); const email = document.querySelector("#email").value; const password = document.querySelector("#password").value; try { const { data } = await axios.post( "http://localhost:3000/api/v1/auth/login", { email, password } ); console.log("token", data.token); localStorage.setItem("token", data.token); } catch (error) { console.log(error); } }); // after submit, if authenticated, goes to protective route btnSubmitDOM.addEventListener("click", async () => { const token = localStorage.getItem("token"); console.log("toekn when press", token); try { const { data } = await axios.get("http://localhost:3000/api/v1/inventories", { headers: { Authorization: `Bearer ${token}`, }, }); location.assign("/inventories"); } catch (error) { localStorage.removeItem("token"); console.log(error); } }); </script>
Backend Code
const jwt = require("jsonwebtoken"); const auth = async (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer")) { res.send("Auth route - Unauthenticated Error"); } const token = authHeader.split(" ")[1]; console.log("token is ", token); try { const payload = jwt.verify(token, 'secret'); // console.log(payload); // attached to the inventories route req.user = { userId: payload.userId, email: payload.email, }; // console.log(req.user) next(); } catch (error) { console.log(error); } }; module.exports = auth;
服务器端错误日志
token is eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2MzY4ZjFiM2U1ODJlYmQ1MjJkODI4MGEiLCJlbWFpbCI6Imt5YXdAZ21haWwuY29tIiwiaWF0IjoxNjY3ODI3NzIzLCJleHAiOjE2NzA0MTk3MjN9.rptgRrYB4TrLQCxbB18JqMHd05LSox-LQuiLJS0L2Gw /Users/kyawmyo/Desktop/software-development/nodejs-&-vanilajs-projects/inventories/middleware/authentication.js:8 const token = authHeader.split(" ")[1]; ^ TypeError: Cannot read properties of undefined (reading 'split')
问题根源与修复方案
后端中间件逻辑漏洞
认证中间件中,当authHeader不存在或格式不符合要求时,调用res.send()后没有终止后续代码执行,导致后续尝试对undefined的authHeader执行split()方法,触发类型错误。修复后的后端认证中间件代码:
const jwt = require("jsonwebtoken"); const auth = async (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer")) { // 发送错误响应后立即返回,终止后续代码执行 return res.status(401).send("Auth route - Unauthenticated Error"); } const token = authHeader.split(" ")[1]; console.log("token is ", token); try { const payload = jwt.verify(token, 'secret'); req.user = { userId: payload.userId, email: payload.email, }; next(); } catch (error) { console.log(error); // 捕获JWT验证失败的情况,返回401状态码 return res.status(401).send("Invalid or expired token"); } }; module.exports = auth;前端逻辑冲突优化
原代码同时绑定了表单submit事件和按钮click事件,可能导致登录请求未完成、Token未存入localStorage时就触发受保护接口请求,此时token为null,构造出无效的请求头。建议将登录与请求受保护接口的逻辑整合:<script> const formDOM = document.querySelector(".form"); // 登录并请求受保护接口 formDOM.addEventListener("submit", async (e) => { e.preventDefault(); const email = document.querySelector("#email").value; const password = document.querySelector("#password").value; try { // 登录获取Token const { data } = await axios.post( "http://localhost:3000/api/v1/auth/login", { email, password } ); console.log("token", data.token); localStorage.setItem("token", data.token); // 登录成功后直接请求受保护接口 const { data: inventoryData } = await axios.get("http://localhost:3000/api/v1/inventories", { headers: { Authorization: `Bearer ${data.token}`, }, }); location.assign("/inventories"); } catch (error) { localStorage.removeItem("token"); console.log(error); } }); </script>
内容的提问来源于stack exchange,提问作者Kyaw Myo
相关产品推荐
相关产品推荐

