写在前面
Token管理 是每一个前端程序员都必须面对的问题,但是如果有一个Token管理模板使我们每次着手一个新项目时都可以傻瓜式管理Token ,那么将可以减轻我们的很多压力,本文就介绍一个React的Token管理模式------ Redux+localStorage
直接开始
一、封装Token工具
封装一些Token工具,可以节省很多的代码量。也使代码更加规范化 ,最常用的Token操作有存储(登录时获取),拿取(请求其它接口时校验Token),删除(退出登录时删除) 等
js
//封装和token相关的方法 存 取 删
const TOKEN_KEY = 'token_key'
function setToken(token) {
localStorage.setItem(TOKEN_KEY, token)
}
function getToken() {
return localStorage.getItem(TOKEN_KEY)
}
function removeToken() {
localStorage.removeItem(TOKEN_KEY)
}
export { setToken, getToken, removeToken }
二、获取用户Token
获取用户Token一般发生在用户登录验证完成之后
js
// 和用户相关的状态管理
import {createSlice} from '@reduxjs/toolkit'
import { setToken as _setToken , getToken,removeToken } from '@/utils'
import { loginAPI,getProfileAPI } from '@/apis/user'
const userStore=createSlice({
name: 'user',
initialState: {
token: getToken() ||'',
userInfo: {}
},
reducers: {
setToken(state, action) {
state.token = action.payload
// 持久化存储token
_setToken(action.payload)
},
setUserInfo(state, action) {
state.userInfo = action.payload
},
clearUserInfo(state) {
state.userInfo = {}
state.token = ''
removeToken()
}
}
})
//解构出actionCreators
const {setToken,setUserInfo,clearUserInfo} = userStore.actions
//获取reducer函数
const userReducer = userStore.reducer
//异步方法 完成登录获取token
const fetchLogin=(loginForm)=>{
return async (dispatch)=>{
//1.发送异步请求
const res=await loginAPI(loginForm)
//2.提交同步action进行token的存入
dispatch(setToken(res.data.token))
}
}
//获取个人信息异步方法
const fetchUserInfo=()=>{
return async (dispatch)=>{
const res=await getProfileAPI()
dispatch(setUserInfo(res.data))
}
}
export {fetchLogin,setToken,fetchUserInfo,clearUserInfo}
export default userReducer
我们在用户的状态管理代码中(Redux)对Token的获取进行了进一步封装(fetchLogin(loginForm))这样做的好处是可以一步到位地完成Token在Redux和localStorage中的存储,而且刷新之后Redux中的Token不为空 ,之后只需在用户点击登录按钮时调用fetchLogin(loginForm)
即可实现用户Token的获取,如下
js
const onFinish = async(values) => {
console.log(values)
// 触发action fetchLogin
await dispatch(fetchLogin(values))
//1.跳转到首页
navigate('/')
//2.提示一下用户
message.success('登录成功')
}
三、在axios中使用Token
这部分的代码基本上在哪个项目中都是固定的,这里只是展示一种方案
jsx
//axios的封装处理
import axios from 'axios'
import { getToken } from '@/utils'
import router from '@/router'
// 1.根域名配置
// 2.超时时间
// 3.请求拦截器/响应拦截器
const request = axios.create({
baseURL: 'http://geek.itheima.net/v1_0',
timeout: 5000
})
// 添加请求拦截器
// 在请求发送之前 做拦截 插入一些自定义的配置[参数的处理]
request.interceptors.request.use((config)=> {
//操作这个config 注入token数据
//1.获取token
//2.按照你好后端的格式要求做token拼接
const token = getToken()
if(token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, (error)=> {
return Promise.reject(error)
})
// 添加响应拦截器
// 在响应返回到客户端之前 做拦截 重点处理返回的数据
request.interceptors.response.use((response)=> {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response.data
}, (error)=> {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
// 判断状态码是否是401 如果是401 表示token失效 跳转到登录页
console.dir(error)
if(error.response.status === 401) {
//跳转到登录页
router.navigate('/login',{replace:true})
window.location.reload()
}
return Promise.reject(error)
})
export { request }
值得注意的有两点
1.添加了请求头Authorization
这样方便每次发起请求后端去校验请求头中的Token是否正确,当然,这也是和后端约定的格式
2.在interceptors
中会判断状态码是否为401 ,这是和后端约定俗成的规则(不出意外的话)代表token失效或不存在 。也就是用户没有登录,或者登录过期,会强行跳转到登录页面重新登录
下面解决一个大家可能会有的疑问,既然Token已经存在了localStorage
为何还要被Redux管理?
1. 避免频繁读取 localStorage
(性能优化)
localStorage
是同步 API,每次读取都会阻塞主线程,频繁读取可能影响性能。- Redux 存储在内存中,访问速度更快,适合高频访问的场景(如每次 API 请求都要带上 Token)。
示例对比
javascript
// ❌ 每次调用 API 都从 localStorage 读取(性能差)
axios.get("/api/data", {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
// ✅ 从 Redux 中读取(内存操作,更快)
const token = useSelector((state) => state.auth.token);
axios.get("/api/data", {
headers: { Authorization: `Bearer ${token}` },
});
2. 保持 UI 与状态的同步
localStorage
不会触发 React 组件更新,如果 Token 变化(如登录/登出),页面不会自动重新渲染。- Redux 状态变化会触发组件更新,确保 UI 及时响应 Token 的变化(如显示/隐藏登录按钮)。
示例场景
javascript
// 登录成功后更新 Redux 和 localStorage
dispatch(loginSuccess(token)); // 更新 Redux
localStorage.setItem("token", token); // 持久化
// 组件中根据 Redux 状态显示不同 UI
const { token } = useSelector((state) => state.auth);
return token ? <Dashboard /> : <Login />;
3. 服务端渲染(SSR)兼容性
localStorage
是浏览器 API ,在 SSR(如 Next.js)环境下无法使用,会导致ReferenceError
。- Redux 可以在服务端初始化状态,避免 SSR 报错。
解决方案
javascript
// 在服务端渲染时,从请求头或 Cookie 初始化 Redux Token
const store = configureStore({
preloadedState: { auth: { token: req.cookies.token } },
});
4. 方便 Token 的全局管理
- Redux 提供统一的状态管理 ,方便在多个组件中共享 Token,避免手动传递
props
。 - 支持中间件处理(如自动刷新 Token、拦截失效 Token)。
示例:Redux 中间件自动添加 Token
javascript
axios.interceptors.request.use((config) => {
const token = store.getState().auth.token;
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
5. 安全性增强
-
localStorage
易受 XSS 攻击,但 Token 仍需持久化存储(如用户刷新页面后保持登录)。 -
Redux 存储的 Token 仅在内存中 ,关闭页面后消失,配合
localStorage
实现:- 短期访问:从 Redux 读取(内存快,适合高频使用)。
- 长期持久化 :从
localStorage
恢复(用户刷新页面后重新初始化 Redux)。
初始化 Redux 的 Token
javascript
// 应用启动时从 localStorage 恢复 Token
const token = localStorage.getItem("token");
const store = configureStore({
reducer: rootReducer,
preloadedState: { auth: { token } },
});
结语
Token 管理 是前端开发中不可忽视的一环,Redux + localStorage 的组合方案既保证了数据的持久化,又优化了性能与状态同步。通过封装工具函数、统一管理 Token 存储与校验,我们可以减少重复代码,提高开发效率,同时增强应用的安全性和可维护性。
无论是小型项目还是企业级应用,这套模式都能灵活适配。希望本文的实践方案能帮助你快速搭建可靠的 Token 管理系统,让登录态管理变得简单高效!