Node.js+Express应用前端JWT存储与Authorization头携带方案咨询
前端JWT存储与携带解决方案
问题背景
我基于Node.js、Express、TypeORM和SQL开发公司内部应用,已有User认证实体,登录流程为:
- 用户访问
www.mysite.com/login - 认证成功后后端生成JWT
- 前端跳转至
/dashboard - 受保护路由通过中间件校验JWT
现有后端登录路由与令牌生成逻辑(代码如下),用Postman测试时认证中间件工作正常,但浏览器中登录后跳转到/dashboard时,中间件返回"Token not provided",问题出在前端JWT的存储与携带方式上。
async function generateToken(user: any) { return jwt.sign( { id: user.id, username: user.username, }, process.env.JWT_SECRET as string, { expiresIn: "1h" }, ); } async function logUser(username: string, password: string) { const userServiceInstance = new userService(); const users = await userServiceInstance.getUsers(); const user = users.find( (u) => u.username === username && u.password === password, ); if (!user) { return null; } else { const token = await generateToken(user); return token; } } router.post("/login", async (req, res) => { const { username, password } = req.body; const token = await logUser(username, password); if (!token) { return res .status(401) .json({ message: "Invalid credentials", token: null }); } return res.json({ message: "Login successful", token }); });
前端JWT存储方案
1. localStorage(持久化存储)
适合需要记住登录状态的场景,关闭浏览器后token仍保留,内部应用XSS风险较低时优先选用:
// 登录请求成功后存储token async function handleLogin(username, password) { const res = await fetch('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await res.json(); if (data.token) { localStorage.setItem('authToken', data.token); window.location.href = '/dashboard'; } }
2. sessionStorage(会话级存储)
仅在当前浏览器会话有效,关闭窗口即清除,适合临时登录场景:
// 登录成功后存储 sessionStorage.setItem('authToken', data.token);
3. Cookie(高安全性)
若后端配合配置安全属性,可避免XSS风险,且请求会自动携带token,适合对安全性要求高的场景:
后端需修改登录接口返回逻辑,设置Cookie:
// 登录接口中替换原res.json逻辑 res.cookie('authToken', token, { httpOnly: true, // 禁止JS读取,防XSS secure: process.env.NODE_ENV === 'production', // 仅HTTPS传输 sameSite: 'strict', // 防CSRF maxAge: 3600000 // 1小时,与token过期时间一致 }); res.json({ message: "Login successful" });
受保护路由/请求携带JWT的方法
1. API请求手动携带token
封装统一请求函数,自动在请求头添加Authorization: Bearer <token>:
// 封装带认证的请求工具 async function authFetch(url, options = {}) { const token = localStorage.getItem('authToken'); // 或sessionStorage.getItem('authToken') const requestOptions = { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${token}` } }; const res = await fetch(url, requestOptions); // 处理token过期 if (res.status === 401) { localStorage.removeItem('authToken'); window.location.href = '/login'; throw new Error('Token已过期,请重新登录'); } return res; } // 示例:dashboard页面获取数据 async function loadDashboardData() { try { const res = await authFetch('/api/dashboard/data'); const data = await res.json(); // 渲染数据 } catch (err) { console.error(err); } }
2. 前端路由守卫(框架场景)
如果使用React/Vue等框架,需添加路由守卫,进入受保护路由前校验token:
React Router示例
import { Navigate } from 'react-router-dom'; export function ProtectedRoute({ children }) { const hasToken = !!localStorage.getItem('authToken'); return hasToken ? children : <Navigate to="/login" replace />; } // 路由配置 <Route path="/dashboard" element={ <ProtectedRoute> <DashboardPage /> </ProtectedRoute> } />
Vue Router示例
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !localStorage.getItem('authToken')) { next('/login'); } else { next(); } }); // 路由配置 { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } }
注意事项
- 必须处理token过期:后端返回401状态码时,前端清除存储的token并跳转至登录页
- 若用localStorage/sessionStorage,需注意XSS防护,内部应用可配置Content-Security-Policy减少风险
- 若用Cookie,后端必须设置
HttpOnly、Secure、SameSite等安全属性,避免CSRF与XSS攻击
内容的提问来源于stack exchange,提问作者Vinicius Ferreira
相关产品推荐
相关产品推荐

