React+Zustand+JWT登录鉴权项目详解:小白也能秒懂的实战流程!
写在前面:
你是不是刚开始学React,想搞懂登录鉴权到底是咋回事?别急!本文带你深入浅出了解用React+Zustand+JWT实现登录鉴权的全过程。讲解细致到每一行代码,小白也能秒懂,保证读完你就能自己写一个小项目啦!
话不多说,开干!
目录
-
- 什么是JWT,为什么用JWT做登录鉴权?
-
- 项目结构和技术栈介绍
-
- 登录流程全解析
-
- Token怎么存储,如何自动带上请求?
-
- 权限路由保护的实现原理
-
- 状态管理(Zustand)全攻略
-
- 代码逐步拆解讲解
-
- 项目中遇到的常见疑问答疑
-
- 总结与扩展建议
1. 什么是JWT,为什么用JWT做登录鉴权?
JWT(JSON Web Token)是一种基于JSON的安全令牌,用来证明用户身份。它像身份证,服务器发给你一个“通行证”,你每次请求都带着它,服务器靠它判断“你是不是合法用户”。
为啥用JWT?
- 轻量:不需要在服务器存会话,减少负担
- 跨域方便:适合SPA、移动端等场景
- 自包含:JWT里带了用户信息,服务器直接解析即可 下面附赠一张图片
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刷新机制、权限动态路由等。
结语
学习编程就像开车,开始总是慢慢踩油门,慢慢掌握技巧。别怕代码复杂,慢慢理顺流程,天天动手写,你也会变成老司机!
你已经迈出了一大步,我为你点赞👏!如果你还想要更深入的改造版或者其他疑问,随时喊我!
欢迎点赞、收藏,转发给更多想学登录鉴权的朋友!
有问题留言,我帮你继续拆!