恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

React + Zustand + JWT:从零实现登录鉴权与请求拦截

  • 首页
  • 资讯中心
  • /
  • React + Zustand + JWT:从零实现登录鉴权与请求拦截

相关资讯

中国技术大败局TBL-20260808-039深度解剖报告V2.1 决策迭代版 2026/8/24 15:02:48
DXVK 快速上手:Linux 上跑 Windows 游戏的完整教程与帧率排查 2026/8/24 14:57:47
我测试了Claude API在国内的真实延迟,结果比你想的更离谱 2026/8/24 14:57:47

最新资讯

原神内存注入外挂实战:Akebi-GC 从 DLL 注入到自动拾取的完整路径
VLA模型本地部署实战:从环境搭建到项目包装的完整指南
抖音AI无人直播小游戏:Python自动化与AI大模型集成实战
用AI自动化Excel与PDF数据处理:Claude+Python实战指南
5分钟让上百个元素自动铺满形状:Illustrator 脚本 Fillinger 随机填充指南
Unlock Music 实操指南:在浏览器里用 5 分钟完成加密音乐文件解密

今日推荐

OpenModScan:免费跨平台 Modbus 主站调试工具,让现场通讯验证一键搞定
WechatHook 终极指南:5大核心能力详解,3分钟看懂微信自动化
如何在ThinkPad X390上安装macOS:OpenCore EFI完整指南

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

React + Zustand + JWT:从零实现登录鉴权与请求拦截

发布时间:2026/8/24 15:02:48
React + Zustand + JWT:从零实现登录鉴权与请求拦截 引言你有没有遇到过这样的场景用户登录成功后刷新页面就丢了登录态或者每个请求都要手动写一遍Authorization头代码又臭又长说白了登录鉴权的本质就是在无状态的 HTTP 协议上建立起一个可信任的身份传递机制。今天这篇文章我会用 React Zustand Axios JWT带你从零搭建一个完整的前端登录鉴权方案并且用 mockjs 模拟后端接口让你读完就能直接落地到自己的项目里。读完你将收获JWT 的 sign / verify 核心原理Zustand 管理全局登录态的实战用法Axios 拦截器自动携带 token 的优雅实现基于 mockjs 的本地后端模拟路由守卫与导航栏登录态展示的完整思路一、JWT无状态 HTTP 的身份令牌HTTP 是无状态的每次请求都是独立的服务器不知道你是谁。所以我们需要一个“身份凭证”让用户在登录后后续每次请求都能证明“我是 admin”。JWTJSON Web Token就是为此而生的。它本质上是一个字符串由三部分组成Header.Payload.SignatureHeader声明加密算法如 HS256Payload存放用户身份信息JSON 对象Signature对前两部分的签名防止篡改JWT 有两个核心动作sign把用户 JSON 对象加密成 token登录时颁发verify把 token 解密还原成 JSON 对象后续请求验证对比传统的 cookie/session 方案JWT 最大的优势是无状态session 方案服务器需要保存 sessionId 和会话对象分布式环境下要共享 session麻烦。JWT 方案任何一台服务器签发的 token在其他服务器上都能用同一个 secret 验签天然适合微服务和分布式架构。下面看我们 mock 后端如何实现 sign 和 verify// mock/user.js 中的登录接口部分 const token jwt.sign( { user: body.username, // payload 放用户信息 role: admin }, secret, // 加密密钥生产环境请放环境变量 { expiresIn: 86400 // 过期时间 24h } )// mock/user.js 中的 /api/repo 接口验证 token const authHeader req.headers?.authorization; if (!authHeader) { return { code: 401, msg: Missing authorization header }; } const token authHeader.split( )[1]; // Bearer xxx try { let decoded jwt.verify(token, secret); // decoded 就是之前 sign 进去的 payload return { code: 0, data: decoded.user } } catch (err) { return { code: 401, msg: Invalid token } }你会发现JWT 就像一个自带身份信息的加密信封前端拿到后每次请求带上它后端验签通过就知道你是谁。二、Zustand轻量全局状态管理登录状态token、user 信息是典型的跨组件、跨路由共享的数据。如果用 React 自带的 Context 也能做但代码会显得啰嗦用 Redux 又太重。Zustand 就是一个非常轻量的选择——API 简单不需要 Provider 包裹直接 hooks 调用。全局状态不要散落在组件里统一交给 store组件只负责渲染和交互。我们创建store/user.jsimport { create } from zustand; export const useAuthStore create(set ({ // 初始化时从 localStorage 读取解决刷新丢状态问题 token: localStorage.getItem(token) || , user: JSON.parse(localStorage.getItem(user)) || null, // 登录成功后设置状态并持久化到 localStorage setAuth: ({ token, user }) { localStorage.setItem(token, token); localStorage.setItem(user, JSON.stringify(user)); set({ token, user }); }, // 退出登录清除状态 logout: () { localStorage.removeItem(token); localStorage.removeItem(user); set({ token: , user: null }); } }))在组件中使用const setAuth useAuthStore(state state.setAuth); const user useAuthStore(state state.user); const token useAuthStore(state state.token); const logout useAuthStore(state state.logout);Zustand 的create返回一个 hook组件里按需订阅状态避免不必要的渲染。这就是它比 Context 更优雅的地方。深入理解 set 方法上面的代码里create的函数参数接收一个set这个set就是 Zustand 用来更新状态的核心方法。理解它你就掌握了 Zustand 的精髓。set的用法非常灵活主要有两种形式1. 直接传入一个对象部分状态set({ token: xxx, user: { name: admin } })这是最常见的写法Zustand 会浅合并这个对象到当前状态中。也就是说你只传需要更新的字段其他字段保持不变。比如setAuth里我们只更新了token和user如果 store 里还有其他状态比如count它们不会被影响。2. 传入一个函数基于旧状态计算新状态set(state ({ count: state.count 1 }))当你需要依赖旧状态来更新时一定要用函数形式。比如计数器、数组追加等场景避免因闭包导致的状态过期问题。在我们的store/user.js中使用的是对象形式因为setAuth和logout都是整体替换token和user不依赖旧值。但如果你的 store 有更复杂的更新逻辑函数形式会更安全。另外注意set是同步执行的调用后 Zustand 会立即通知所有订阅了相关状态的组件重新渲染。所以不需要await也不要在set后面写依赖新状态的同步逻辑不过实际上状态已经更新了可以读取到最新值。再看另一个例子store/todos.jsimport { create } from zustand; export const useTodosStore create(set ({ todos: [], setTodos: (todos) { set({ todos }); } }))这里setTodos也是用对象形式整体替换todos数组。如果要在原数组上追加就可以改成函数形式addTodo: (todo) set(state ({ todos: [...state.todos, todo] }))一句话总结set是 Zustand 的状态更新入口对象形式用于普通更新函数形式用于依赖旧状态的更新。掌握它你的 store 就能灵活应对各种场景。三、mockjs前端独立开发的后端模拟器前后端分离开发时后端接口可能还没好。用vite-plugin-mock可以轻松在本地模拟接口让你专注于前端逻辑。安装配置// vite.config.js import { defineConfig } from vite import react from vitejs/plugin-react import { viteMockServe } from vite-plugin-mock export default defineConfig({ plugins: [ react(), viteMockServe({ mockPath: mock, // mock 文件目录 localEnabled: true // 本地开发启用 }) ], })然后在mock/user.js中定义接口。我们有两个接口POST /api/login验证用户名密码签 tokenGET /api/repo需要携带 token验签后返回数据完整的 mock 代码上面已经展示过了这里不再重复。需要注意的是mock 接口的路径要和 axios 的 baseURL 匹配我们后面会设置baseURL: /api这样请求/login实际就是/api/login。四、登录流程实战表单验证 状态更新登录页面是鉴权的入口我们来看核心逻辑// pages/Login.jsx 关键部分 import React, { useState, useEffect } from react; import { useNavigate, useLocation } from react-router-dom; import { login } from ../api/user; import { useAuthStore } from ../store/user; function Login() { const navigate useNavigate(); const location useLocation(); const from location.state?.from || /; // 记录来源页面 const setAuth useAuthStore(state state.setAuth); const [formData, setFormData] useState({ username: , password: }); const [errors, setErrors] useState({ username: , password: }); const [isValid, setIsValid] useState(false); // 表单验证逻辑略 const handleLogin async e { e.preventDefault(); try { const res await login(formData); // 调用 API if (res.code 0) { setAuth({ token: res.token, user: res.user }); // 存入全局状态 navigate(from, { replace: true }); // 跳转回原页面 } else { alert(res.message || 登录失败); } } catch (err) { console.error(err); alert(登录失败); } }; // 渲染表单略 }登录成功后token 和 user 同时写入了 localStorage 和 Zustand store。这样即使用户刷新页面也能从 localStorage 恢复登录态。注意真实项目中res.user应该由后端返回完整用户信息不要只信任前端传的用户名。五、Axios 拦截器自动携带 token如果每次请求都要手动写axios.get(/repo, { headers: { authorization: Bearer ${token} } })那代码会非常重复且容易遗漏。Axios 拦截器就是来解决这个问题的。// api/config.js import axios from axios; const instance axios.create({ baseURL: /api, timeout: 5000 }); // 请求拦截器每个请求发出前自动附加 token instance.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[authorization] Bearer ${token}; } return config; }); // 响应拦截器直接返回 res.data简化调用 instance.interceptors.response.use(res { return res.data; }); export default instance;拦截器就像一道安检门每个请求都要经过它——请求前检查有没有 token有就带上响应后统一解包业务代码直接拿数据。这样业务 API 函数就变得非常干净// api/repo.js import axios from ./config; export const getRepo async () { const res await axios.get(/repo); return res; }六、路由守卫保护敏感页面有些页面如支付页/pay必须登录后才能访问。我们需要一个路由守卫组件// components/RequireAuth.jsx import { Navigate } from react-router-dom; import { useAuthStore } from ../store/user; function RequireAuth({ children }) { const token useAuthStore(state state.token); if (!token) { return Navigate to/login replace /; } return {children}/; } export default RequireAuth;然后在路由中包裹需要保护的页面// App.jsx Route path/pay element{ RequireAuth Pay / /RequireAuth } /这样未登录用户访问/pay就会被重定向到/login。进阶优化如果你希望登录后能跳回用户原本想访问的页面可以在RequireAuth中传递state// 改进版 RequireAuth import { Navigate, useLocation } from react-router-dom; import { useAuthStore } from ../store/user; function RequireAuth({ children }) { const token useAuthStore(state state.token); const location useLocation(); if (!token) { return Navigate to/login state{{ from: location }} replace /; } return {children}/; }这样登录页中的location.state?.from就能拿到原始路径实现“从哪里来回哪里去”的体验。七、导航栏登录态的条件渲染导航栏是展示登录状态的绝佳位置。根据 token 和 user 的不同显示不同的链接和按钮// components/Nav.jsx import { Link } from react-router-dom; import { useAuthStore } from ../store/user; function Nav() { const token useAuthStore(state state.token); const user useAuthStore(state state.user); const logout useAuthStore(state state.logout); const handleLogout () { logout(); // 可选跳转到首页 // window.location.href /; }; return ( nav style{{ padding: 0, borderBottom: 1px solid #ccc }} Link to/Home/Link Link to/payPay/Link {!token Link to/loginLogin/Link} {user a{user.name}/a} {token button onClick{handleLogout}Logout/button} /nav ); } export default Nav;登录态的管理不仅仅是为了鉴权更是为了提供个性化的用户体验。导航栏根据 token 和 user 动态展示让用户一眼就知道自己是否登录、是谁。八、完整流程串联让我们把整个流程串起来用户登录输入用户名密码提交/api/login后端签发 token验证通过后用jwt.sign生成 token 并返回前端存储状态setAuth将 token 和 user 存入 Zustand store 和 localStorage后续请求自动带 tokenAxios 请求拦截器从 localStorage 读取 token写入authorization头后端验证 token需要鉴权的接口用jwt.verify验签返回用户数据路由守卫保护页面未登录访问敏感页面自动跳转登录导航栏动态展示根据登录态显示不同内容整个方案的核心思想是状态统一管理请求统一拦截页面统一守卫。九、避坑指南与总结常见坑点localStorage 的 XSS 风险token 存在 localStorage 中如果网站存在 XSS 漏洞攻击者可以窃取 token。更安全的做法是使用 httpOnly cookie但那需要后端配合设置。对于多数内部系统或学习项目localStorage 足够方便。token 过期处理JWT 有过期时间expiresIn过期后后端会返回 401。前端可以在响应拦截器中统一处理 401比如清除状态并跳转登录页。本文示例没有处理实际项目中应该加上。刷新页面状态初始化Zustand 初始化时从 localStorage 读取这是同步操作不会导致组件初始渲染时状态缺失。但如果用户信息很大可以考虑只存 token用户信息通过接口获取。secret 安全mock 中的 secret 是硬编码的生产环境一定要放在环境变量中并且定期更换。总结我们用一套简洁的技术栈React Zustand Axios JWT实现了完整的登录鉴权流程。核心代码覆盖了JWT 的 sign / verifyZustand 全局状态管理含set方法解析Axios 请求/响应拦截器mockjs 本地模拟接口路由守卫保护页面导航栏登录态展示技术选型没有银弹但适合自己的就是最好的。Zustand 足够轻JWT 足够通用Axios 拦截器足够优雅这套组合拳非常适合中小型 React 项目快速落地。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号