NodeJS认证系统跳转请求无法携带JWT Token的问题咨询
NodeJS JWT认证系统:后续请求携带Token问题
问题背景
搭建了一套NodeJS认证系统,核心逻辑如下:
/auth/register处理用户注册/auth/login验证用户身份后生成JWT Token并返回- 除上述两个路由及视图渲染路由外,其余所有路由需通过
authMiddleware中间件校验:检查请求头是否包含带Token的Authorization字段,存在则放行,否则抛出401错误
目前遇到的问题:登录视图提交表单后,成功调用/auth/login获取Token并存入localStorage,但跳转至/home页面时,请求头未携带Token,触发401错误。不清楚如何在后续请求中传递Token,也不确定当前用法是否正确。
相关代码
控制器代码
const AuthenticationError = require("../errors/authError"); const jwt = require("jsonwebtoken"); const User = require("../models/user"); module.exports = { renderRegisterPage: (req, res, next) => { res.render("register"); }, register: async ({ body: { username, password, email } }, res, next) => { try { const newUser = new User({ username, password, email }); await newUser.save(); res.status(200).end(); } catch (error) { next(error); } }, renderLoginPage: (req, res, next) => { res.render("login"); }, login: async (req, res, next) => { const { username, password } = req.body; try { const user = await User.findOne({ username }); if (!user) throw new AuthenticationError("User not found : invalid username", 404); const passwordMatch = await user.comparePassword(password); if (!passwordMatch) throw new AuthenticationError("Incorrect password", 401); const token = jwt.sign({ userId: user.id }, process.env.SECRET_KEY, { expiresIn: "1h", }); res.status(200).json({ token }); } catch (error) { next(error); } }, };
authMiddleware代码
const jwt = require("jsonwebtoken"); const User = require("../models/user"); const AuthenticationError = require("../errors/authError"); module.exports = async (req, res, next) => { const token = req.headers.authorization?.split(" ")[1]; try { console.log(token); if (!token) throw new AuthenticationError("Authentication required", 401); const decodedToken = jwt.verify(token, process.env.SECRET_KEY); const user = await User.findById(decodedToken.userId); if (!user) throw new AuthenticationError("User not found", 404); req.user = user; next(); } catch (error) { next(error); } };
登录视图代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="/styles/auth.css" /> <title>Login</title> </head> <body> <h1>Login</h1> <form id="loginForm"> <label for="username">Username:</label> <input type="text" id="username" name="username" required /> <label for="password">Password:</label> <input type="password" id="password" name="password" required /> <button type="submit">Login</button> </form> <script> document .getElementById("loginForm") .addEventListener("submit", function (event) { event.preventDefault(); var formData = { username: document.getElementById("username").value, password: document.getElementById("password").value, }; fetch("http://localhost:6543/auth/login", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(formData), }) .then((response) => response.json()) .then(({ token }) => { localStorage.setItem("token", token); window.location.href = "/home"; }) .catch((error) => { console.log("Error:", error); }); }); </script> </body> </html>
解决方案
1. 区分页面请求与API请求的Token携带逻辑
window.location.href = "/home"是浏览器发起的页面渲染请求,这类请求不会自动携带localStorage中的Token。而前端发起的AJAX/fetch请求属于API请求,需要手动配置请求头。
2. 处理页面渲染请求的认证(如/home页面)
如果/home是服务器渲染的页面,需要让浏览器在页面请求时自动携带Token,最常用的方式是将Token存入Cookie:
修改登录视图的跳转逻辑
在存入localStorage的同时,把Token写入Cookie:
.then(({ token }) => { localStorage.setItem("token", token); // 将Token写入Cookie,浏览器会自动在页面请求时携带 document.cookie = `token=${token}; path=/; max-age=3600`; window.location.href = "/home"; })
修改authMiddleware支持从Cookie取Token
更新中间件代码,优先从Authorization头取Token,其次从Cookie取:
const token = req.headers.authorization?.split(" ")[1] || req.cookies.token;
注意:若使用Cookie,建议根据场景添加安全属性,比如
secure(HTTPS环境下)、sameSite=Strict防止CSRF,若不需要前端读取Token可添加httpOnly。
3. 处理API请求的Token携带
对于前端发起的API请求,封装一个通用的fetch函数,自动带上Token:
// 封装带认证的fetch function authenticatedFetch(url, options = {}) { const token = localStorage.getItem("token"); const headers = { ...options.headers, // 若存在Token,添加Authorization头(Bearer格式) ...(token ? { Authorization: `Bearer ${token}` } : {}) }; return fetch(url, { ...options, headers }); }
之后所有需要认证的API请求都使用这个函数,比如:
// 示例:获取用户信息 authenticatedFetch("/api/user/profile") .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.error(err));
4. 当前代码的正确性验证
- 控制器与authMiddleware的核心逻辑是正确的:JWT生成、Token解析校验、用户关联流程均符合规范。
- 登录视图的问题仅在于未处理后续请求的Token携带逻辑,补充上述代码即可解决401问题。
内容的提问来源于stack exchange,提问作者Maxime Arnould
相关产品推荐
相关产品推荐

