React全家桶实战:一步步教你用Zustand+JWT打造登录鉴权系统

702 阅读4分钟

React+Zustand+JWT登录鉴权项目详解:小白也能秒懂的实战流程!

写在前面:
你是不是刚开始学React,想搞懂登录鉴权到底是咋回事?别急!本文带你深入浅出了解用React+Zustand+JWT实现登录鉴权的全过程。讲解细致到每一行代码,小白也能秒懂,保证读完你就能自己写一个小项目啦!
话不多说,开干!


目录

    1. 什么是JWT,为什么用JWT做登录鉴权?
    1. 项目结构和技术栈介绍
    1. 登录流程全解析
    1. Token怎么存储,如何自动带上请求?
    1. 权限路由保护的实现原理
    1. 状态管理(Zustand)全攻略
    1. 代码逐步拆解讲解
    1. 项目中遇到的常见疑问答疑
    1. 总结与扩展建议

1. 什么是JWT,为什么用JWT做登录鉴权?

JWT(JSON Web Token)是一种基于JSON的安全令牌,用来证明用户身份。它像身份证,服务器发给你一个“通行证”,你每次请求都带着它,服务器靠它判断“你是不是合法用户”。

为啥用JWT?

  • 轻量:不需要在服务器存会话,减少负担
  • 跨域方便:适合SPA、移动端等场景
  • 自包含:JWT里带了用户信息,服务器直接解析即可 下面附赠一张图片

dc5d4813db870578c1e6681845385037.jpg


2. 项目结构和技术栈介绍

  • React:前端UI库
  • Zustand:轻量级全局状态管理库,管理登录状态和用户信息
  • axios:HTTP请求库,搭配请求拦截器自动携带token
  • jsonwebtoken:用于生成和解析JWT的库(模拟后端生成和校验)
  • react-router-dom:前端路由,负责导航和路由守卫

3. 登录流程全解析

3.1 用户输入用户名和密码

前端用非受控组件(useRef)拿到用户名密码:

const username = usernameRef.current.value;
const password = passwordRef.current.value;

为什么不用useState?因为简单,用ref能快速拿到输入框值,且性能更好(不会因为输入触发多次渲染)。


3.2 调用登录接口 /api/login

前端调用 doLogin({ username, password }),本质是发POST请求给mock接口。


3.3 mock接口校验

if(username !== 'admin' && password !== '123456'){
    return { code:1, message:'用户名或密码错误' }
}
const token = jwt.sign({ user: { id: "001", username: 'admin' } }, secret, { expiresIn: 86400 });
return {
    token,
    data: { id: "001", username: 'admin' }
}

后端判断用户名密码,成功返回JWT和用户信息。


3.4 前端拿到token和用户信息

localStorage.setItem('token', token);
set({ user, isLogin: true });

token存在localStorage,用户信息存在Zustand状态。


4. Token怎么存储,如何自动带上请求?

  • 登录成功后把token放进localStorage,方便后续请求使用。
  • 使用axios请求拦截器:
axios.interceptors.request.use(config => {
    const token = localStorage.getItem('token') || '';
    if(token){
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
});

这样每次发请求自动带token,后台根据token鉴权。


5. 权限路由保护的实现原理

RequireAuth组件封装受保护页面:

const RequireAuth = ({ children }) => {
    const { isLogin } = useUserStore();
    const navigate = useNavigate();
    useEffect(() => {
        if(!isLogin){
            navigate('/login', { from: location.pathname });
        }
    }, []);
    return children;
}

未登录访问 /pay 会自动跳转登录页。


6. 状态管理(Zustand)全攻略

create创建store管理登录状态和用户数据:

export const useUserStore = create(set => ({
    user: null,
    isLogin: false,
    login: async ({ username, password }) => {
        const res = await doLogin({ username, password });
        localStorage.setItem('token', res.data.token);
        set({ user: res.data.data, isLogin: true });
    },
    logout: () => {
        localStorage.removeItem('token');
        set({ user: null, isLogin: false });
    }
}));

7. 代码逐步拆解讲解

7.1 Login页

<form onSubmit={handleLogin}>
    <input ref={usernameRef} placeholder="请输入用户名" />
    <input ref={passwordRef} placeholder="请输入密码" type="password" />
    <button type="submit">登录</button>
</form>
  • 点击登录触发handleLogin
  • 校验非空
  • 调用login方法登录,登录成功后跳首页

7.2 NavBar显示登录状态

{
    isLogin ? (
        <span>欢迎: {user.username} <button onClick={logout}>退出</button></span>
    ) : (
        <Link to="/login">登录</Link>
    )
}

7.3 Axios拦截器

请求时自动带token,响应时打印日志:

axios.interceptors.response.use(res => {
    console.log('请求响应回来了');
    return res;
});

7.4 Mock /api/user接口(虽然没用到)

根据请求头token解码用户信息:

const token = req.headers["authorization"].split(' ')[1];
const decode = jwt.decode(token, secret);
return { code: 0, data: decode.user };

8. 项目中遇到的常见疑问答疑

Q1: 为什么登录接口直接返回user和token?

  • 方便演示,省略了再调一次getUser的步骤。
  • 实际项目建议先拿token,再调用用户信息接口,保证安全和数据一致性。

Q2: ref.current.value是干啥?

  • 拿到非受控组件输入框的当前值。

Q3: axios请求拦截器里token是哪里来的?

  • localStorage拿出来的。

Q4: 为什么用setTimeout跳转首页?

  • 等登录状态设置完成后,给React渲染一点时间再跳转。

9. 总结与扩展建议

  • 通过这个项目你学会了完整的登录鉴权流程。
  • 你知道了JWT的原理、token存储与请求携带。
  • 你掌握了全局状态管理和路由保护。
  • 后续可以尝试:刷新自动登录、token刷新机制、权限动态路由等。

结语

学习编程就像开车,开始总是慢慢踩油门,慢慢掌握技巧。别怕代码复杂,慢慢理顺流程,天天动手写,你也会变成老司机!
你已经迈出了一大步,我为你点赞👏!如果你还想要更深入的改造版或者其他疑问,随时喊我!


欢迎点赞、收藏,转发给更多想学登录鉴权的朋友!
有问题留言,我帮你继续拆!