一、技术设计
本项目采用React19+Zustand5+JWT技术栈,通过模块化设计实现完整的登录鉴权流程。核心架构分为:
- 状态管理层:使用Zustand创建全局用户状态
- 网络请求层:基于Axios封装带token自动注入的请求
- 接口模拟层:通过Vite Mock实现无需后端的接口模拟
- 路由守卫层:利用React Router DOM实现页面级权限控制
二、核心实现
1. Zustand状态管理
// src/store/user.js
import { create } from 'zustand'
import { doLogin } from '../api/user'
export const useUserStore = create((set) => ({
user: null,
isLogin: false,
login: async ({ username, password }) => {
const res = await doLogin({ username, password })
const { token, data: user } = res.data
localStorage.setItem('token', token)
set({ user, isLogin: true })
},
logout: () => {
localStorage.removeItem('token')
set({ user: null, isLogin: false })
}
}))
通过create函数创建响应式状态仓库,将登录状态与用户数据统一管理。异步操作与localStorage持久化结合,确保页面刷新状态不丢失。
2. Axios请求拦截配置
// src/api/config.js
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:5173'
axios.interceptors.request.use(config => {
console.log('请求开始')
const token = localStorage.getItem('token') || ''
config.headers.Authorization = `Bearer ${token}`
return config
})
export default axios
在请求拦截器中自动注入token,实现请求的无感鉴权。通过设置基础URL统一接口前缀,简化请求配置。
3. JWT接口模拟
// mock/login.js
import { sign, decode } from 'jsonwebtoken'
const secret = '!&5d646hcode'
export default [
// 登录接口
{
url: '/api/login',
method: 'post',
response: (req) => {
const { username, password } = req.body
// 生成带过期时间的token
const token = sign(
{ user: { id: "001", username } },
secret,
{ expiresIn: '2h' }
)
return {
code: 0,
data: { token, userInfo: { id: "001", username } }
}
}
},
// 用户信息接口
{
url: '/api/user',
method: 'get',
response: (req) => {
const authHeader = req.headers["authorization"]
if (!authHeader) return { code: 1, message: '缺少token' }
try {
const token = authHeader.split(' ')[1]
const decoded = decode(token, secret)
return { code: 0, data: { userInfo: decoded.user } }
} catch (err) {
return { code: 1, message: 'token无效' }
}
}
}
]
- 使用jsonwebtoken库实现token的签发与验证
- 在mock接口中模拟token过期校验流程
- 采用Bearer模式传递token
- 错误处理机制
测试
/api/login:
/api/user:
4. 登录组件
// src/views/Login/index.jsx
import { useRef } from 'react'
import { useUserStore } from '../../store/user'
import { useNavigate } from 'react-router-dom'
const Login = () => {
const usernameRef = useRef()
const passwordRef = useRef()
const { login } = useUserStore()
const navigate = useNavigate()
const handleLogin = (e) => {
e.preventDefault()
const username = usernameRef.current.value
const password = passwordRef.current.value
login({ username, password }).then(() => {
navigate('/')
}).catch(() => {
alert('登录失败')
})
}
return (
<form onSubmit={handleLogin}>
{/* 输入框省略 */}
<button type="submit">登录</button>
</form>
)
}
- 使用useRef获取表单数据
- 结合React Router实现登录跳转
- 状态变更自动触发路由更新
5. 路由守卫
// src/components/RequireAuth/index.jsx
import { useEffect } from 'react'
import { useUserStore } from '../../store/user'
import { useNavigate } from 'react-router-dom'
export default function RequireAuth({ children }) {
const { isLogin } = useUserStore()
const navigate = useNavigate()
useEffect(() => {
if (!isLogin) {
navigate('/login', { replace: true })
}
}, [isLogin, navigate])
return isLogin ? children : null
}
实现原理:
- 通过useEffect监听登录状态变化
- 未登录时强制跳转到登录页
- 与路由系统深度集成实现页面级权限控制
三、异常处理与优化
1. Token过期处理方案
// src/api/config.js
axios.interceptors.response.use(
response => {
if (response.data.code === 401) {
useUserStore.getState().logout()
alert('登录已过期,请重新登录')
}
return response
}
)
处理策略:
- 在响应拦截器统一处理401错误
- 自动触发登出操作
- 弹窗提示用户重新登录
2. 登录状态持久化
// src/store/user.js
// 使用localStorage持久化存储
localStorage.setItem('token', token)
持久化优势:
- 页面刷新不丢失登录状态
- 支持跨页面共享登录状态
- 简单易用的键值存储
3. 请求并发控制
// src/api/config.js
const api = axios.create({
baseURL: 'http://localhost:5173',
timeout: 10000,
headers: { 'X-Custom-Header': 'foobar' }
})
配置建议:
- 设置全局超时时间
- 添加自定义请求头
- 创建独立实例提升可维护性
四、完整流程图解
用户登录 -> 调用login方法 -> 发送登录请求 ->
服务端验证 -> 生成JWT -> 返回token ->
存储token -> 更新状态 -> 路由跳转 ->
请求拦截自动注入token -> 接口鉴权通过
五、总结
本方案通过Zustand实现的状态管理、Axios的请求拦截、JWT的无状态鉴权,构建了一个完整且可扩展的前端鉴权体系。结合Vite的Mock功能,即使在没有后端支持的情况下也能快速验证业务流程。通过路由守卫和组件状态的联动,实现了细粒度的权限控制。