前端认证入门:基于JWT的身份认证演示项目

在上一篇文章中我们初步实现了基于 JSON Web Token (JWT) 的身份认证机制, 在这篇文章中我们将要通过该机制搭建一个演示项目,其中包括以下技术:

  • React Router :强大的页面路由管理库,负责页面之间的导航与参数传递,包括 BrowserRouter , Routes , Router 组件及 useNavigateuseLocation 等实用钩子

  • React.lazy + Suspense :实现组件懒加载,显著减小应用初始加载体积,提升用户体验

  • 路由保护机制 :通过 RequireAuth 组件实现,未登录用户访问受保护路由时自动重定向至登录页

  • localStorage :客户端存储 JWT token,实现会话状态保持

  • Zustand :轻量级状态管理库,简洁高效地管理用户认证状态

  • Axios :优雅的HTTP请求库,封装API调用并处理请求拦截

下面这是已经搭建好的项目目录

为了遵循了模块化 的原则,我们需要把 API 请求集中到 api/ 目录,这样所有的接口调用和拦截器配置都在一个地方,后期维护起来很方便。用户相关的登录、获取信息接口,都放在 api/user.js 里,统一管理。

静态资源放在 assets/ 目录,让代码和资源分开,保持目录整洁。静态资源放 assets/ ,组件放 components/ ,页面放 views/ ,状态管理放 store/ ,这样分工明确,找代码的时候一目了然。

核心功能实现

1. 路由配置与懒加载

在 App.jsx 中,我们使用 React.lazy 与 Suspense 实现组件懒加载,并配置路由规则:

jsx 复制代码
// 导入 React 核心库中的状态管理、生命周期钩子和代码分割相关组件
import { useState, useEffect, lazy, Suspense } from 'react';
// 导入 React Router 的核心组件,用于实现单页面应用的路由功能
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
// 导入自定义的导航栏组件
import NavBar from './components/NavBar';

// 使用 React.lazy 实现组件的懒加载,提高应用加载性能
const Home = lazy(() => import('./views/Home'));
const Login = lazy(() => import('./views/Login'));
const Pay = lazy(() => import('./views/Pay'));
// 导入需要身份验证的高阶组件
const RequireAuth = lazy(() => import('./components/RequireAuth'));

// 应用根组件
function App() {
  return (
    // Suspense 组件包裹懒加载组件,在组件加载过程中显示加载状态
    <Suspense fallback={<div>Loading...</div>}>
      {/* 全局导航栏,所有页面共享 */}
      <NavBar />
      {/* Routes 组件用于匹配当前 URL 并渲染对应的组件 */}
      <Routes>
        {/* 根路径,渲染首页组件 */}
        <Route path="/" element={<Home />} />
        {/* 登录页面,无需身份验证即可访问 */}
        <Route path="/login" element={<Login />} />
        {/* 支付页面,使用 RequireAuth 高阶组件包裹,确保用户已登录 */}
        <Route path="/pay" element={
          <RequireAuth>
            <Pay />
          </RequireAuth>
        } />
      </Routes>
    </Suspense>
  );
}
export default App;

我们在这里借助 React 及 React Router 相关库,通过lazySuspense实现组件懒加载,利用RoutesRoute进行路由配置,以RequireAuth高阶组件实现支付页面的身份验证保护,同时用NavBar作为全局导航栏,还在Suspense中设置加载状态提示,整体构建了一个具备路由管理、组件懒加载和身份验证功能的单页面应用框架。

2. 用户状态管理

使用 Zustand 创建用户状态管理store,处理登录与登出逻辑:

js 复制代码
// 从 zustand 库中导入 create 方法,用于创建状态管理 store
import { create } from 'zustand';
// 从自定义的用户相关 API 模块中导入 doLogin 方法,用于处理登录请求
import { doLogin } from '../api/user';

// 使用 create 方法创建一个用户状态管理 store,命名为 useUserStore
export const useUserStore = create(set => ({
  // 初始化用户信息为 null
  user: null,        
  // 初始化登录状态为未登录(false)
  isLogin: false,    
  // 定义异步的 login 方法,接收包含 username 和 password 的对象作为参数,默认值为空字符串
  login: async ({ username = "", password = "" }) => {
    // 调用 doLogin 方法发送登录请求,传入用户名和密码
    const res = await doLogin({ username, password });
    // 从响应数据中解构出 token 和用户信息 data(重命名为 user)
    const { token, data: user } = res.data;
    // 将获取到的 token 存储到浏览器的 localStorage 中
    localStorage.setItem('token', token);
    // 使用 set 方法更新 store 中的 user 和 isLogin 状态
    set({
      user,
      isLogin: true
    });
  },
  // 定义 logout 方法,用于处理用户登出逻辑
  logout: () => {
    // 从 localStorage 中移除存储的 token
    localStorage.removeItem('token');
    // 使用 set 方法将 store 中的 user 重置为 null,isLogin 重置为 false
    set({
      user: null,
      isLogin: false
    });
  }
}));

在这里我们使用 Zustand 状态管理库创建了一个用户状态存储,定义了 userisLogin 两个核心状态,提供 loginlogout 两个方法,通过调用登录 API 获取 token 和用户信息并存储到本地存储和状态中,实现登录状态管理与持久化,方便在整个应用中共享和访问用户认证状态。

3. 路由保护组件

实现 RequireAuth 组件,检查用户登录状态:

jsx 复制代码
// 导入用户状态管理库,获取登录状态
import { useUserStore } from '../../store/user';
// 导入React的副作用钩子,用于监听状态变化
import { useEffect } from 'react';
// 导入React Router的导航和位置钩子
import { useNavigate, useLocation } from 'react-router-dom';

/**
 * 身份验证高阶组件:确保只有登录用户才能访问其包裹的组件
 * @param {ReactNode} children - 被保护的子组件
 */
const RequireAuth = ({ children }) => {
  // 从状态管理库中获取用户登录状态
  const { isLogin } = useUserStore();
  // 获取导航函数,用于页面跳转
  const navigate = useNavigate();
  // 获取当前URL路径信息
  const { pathname } = useLocation();

  // 监听登录状态和路径变化
  useEffect(() => {
    // 如果用户未登录
    if (!isLogin) {
      // 重定向到登录页,并通过state传递原始请求路径
      navigate('/login', { state: { from: pathname } });
    }
  }, [isLogin, navigate, pathname]);

  // 已登录用户正常渲染子组件
  return <>{children}</>;
};

export default RequireAuth;

在这里我们通过监听用户登录状态 isLogin,在用户未登录时自动将其重定向到登录页面,并携带当前路径信息以便登录后返回。该组件利用 React Router 的钩子获取路由信息,并结合 Zustand 状态管理库提供的全局状态实现身份验证拦截,确保受保护的路由只能被已登录用户访问。

4. API请求封装

使用 Axios 封装API请求,并添加请求拦截器携带token:

js 复制代码
// config.js
import axios from 'axios';

const instance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 从localStorage获取token
    const token = localStorage.getItem('token');
    if (token) {
      // 添加token到请求头
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

export default instance;
js 复制代码
// user.js
import axios from './config';

export const doLogin = (data) => {
  return axios.post('/login', data);
};

export const getUserInfo = () => {
  return axios.get('/user');
};

这一部分我们在上一篇文章已经实现过,这里不再赘述。

实战

让我们启动项目看看实际效果怎么样:

主界面

登陆页面

点击 Login 按钮,页面自动跳转到了登陆页面:

接着输入用户名和密码:

完成登录:

打开控制台:

这说明 请求发送 → 拦截器处理 → 成功响应 → token解析 → 状态更新 这五步已经成功实现。

小结

该项目基于React 18 + Vite构建了一个JWT认证演示系统,集成React Router路由管理、Zustand轻量状态管理和Axios请求拦截,实现了登录认证、路由守卫、状态持久化等核心功能,模块化设计提升了代码可维护性。

相关推荐
PineappleCoder1 小时前
性能优化与状态管理:React的“加速器”与“指挥家”
前端·react.js
讨厌吃蛋黄酥1 小时前
深度解析:useContext + useReducer — React官方状态管理的终极之道
javascript·react.js·前端框架
bug_kada1 小时前
全家桶开发之Zustand:轻量级状态管理
前端·react.js
伍哥的传说4 小时前
React性能优化终极指南:memo、useCallback、useMemo全解析
前端·react.js·性能优化·usecallback·usememo·react.memo·react devtools
土豆125013 小时前
React Router 相对路径避坑指南:v5 到 v6 的颠覆性变革!
react.js
三月的一天14 小时前
React+threejs两种3D多场景渲染方案
前端·react.js·前端框架
十盒半价16 小时前
React 项目实战:从 0 到 1 构建高效 GitHub 仓库管理应用 —— 基于 React 全家桶的全栈开发指南
前端·react.js·trae
讨厌吃蛋黄酥18 小时前
React高手都在用的秘密武器:Fragment的5大逆天功能,让你的性能飙升200%!
前端·javascript·react.js
薛定谔的算法18 小时前
为什么要有React?从“一万行灾难”到“十万行也很好”
前端·react.js·前端框架
伍哥的传说20 小时前
vite+vue3 工程-SVG图标配置使用指南——vite-plugin-svg-icons 插件
前端·vue.js·react.js·前端框架·vite-plugin-svg·vite 7·vite-plugin