React+Zustand+JWT实现登录鉴权

185 阅读3分钟

一、技术设计

本项目采用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无效' }
      }
    }
  }
]
  1. 使用jsonwebtoken库实现token的签发与验证
  2. 在mock接口中模拟token过期校验流程
  3. 采用Bearer模式传递token
  4. 错误处理机制

测试

/api/login:

屏幕截图 2025-07-24 220445.png

/api/user:

image.png

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>
  )
}
  1. 使用useRef获取表单数据
  2. 结合React Router实现登录跳转
  3. 状态变更自动触发路由更新

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 -> 接口鉴权通过

屏幕录制 2025-07-24 220148.gif

五、总结

本方案通过Zustand实现的状态管理、Axios的请求拦截、JWT的无状态鉴权,构建了一个完整且可扩展的前端鉴权体系。结合Vite的Mock功能,即使在没有后端支持的情况下也能快速验证业务流程。通过路由守卫和组件状态的联动,实现了细粒度的权限控制。