如何实现JWT验证全局检查?React+Django项目优化需求
全局JWT认证检查实现方案
1. 创建全局认证上下文(AuthContext)
通过React Context统一管理用户状态和认证逻辑,彻底避免在每个页面重复编写认证代码:
import { createContext, useContext, useState, useEffect } from 'react'; import axiosInstance from './axiosInstance'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); // 全局检查令牌有效性并同步用户信息 const checkAuth = async () => { setIsLoading(true); const accessToken = localStorage.getItem('access_token'); const refreshToken = localStorage.getItem('refresh_token'); if (!accessToken || !refreshToken) { setUser(null); setIsLoading(false); return; } try { // 尝试用当前access token获取用户信息 const userRes = await axiosInstance.get('/auth/user/'); setUser(userRes.data); } catch (err) { // access token过期,尝试刷新令牌 if (err.response?.status === 401) { try { const refreshRes = await axiosInstance.post('/auth/refresh/', { refresh: refreshToken }); localStorage.setItem('access_token', refreshRes.data.access); // 刷新成功后重新获取用户信息 const newUserRes = await axiosInstance.get('/auth/user/'); setUser(newUserRes.data); } catch (refreshErr) { // refresh token失效,清除存储并登出 localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); setUser(null); } } else { // 其他认证错误,直接登出 localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); setUser(null); } } finally { setIsLoading(false); } }; // 统一登出函数 const logout = () => { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); setUser(null); }; // 应用初始化时执行一次认证检查 useEffect(() => { checkAuth(); }, []); // 初始化Axios拦截器,全局处理令牌逻辑 useEffect(() => { // 请求拦截器:自动添加Authorization头 const reqInterceptor = axiosInstance.interceptors.request.use( (config) => { const token = localStorage.getItem('access_token'); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }, (err) => Promise.reject(err) ); // 响应拦截器:自动处理401令牌过期 const resInterceptor = axiosInstance.interceptors.response.use( (res) => res, async (err) => { const originalReq = err.config; // 仅处理未重试过的401错误 if (err.response?.status === 401 && !originalReq._retry) { originalReq._retry = true; const refreshToken = localStorage.getItem('refresh_token'); try { const refreshRes = await axiosInstance.post('/auth/refresh/', { refresh: refreshToken }); localStorage.setItem('access_token', refreshRes.data.access); // 更新原请求的令牌并重试 originalReq.headers.Authorization = `Bearer ${refreshRes.data.access}`; // 同步用户状态 await checkAuth(); return axiosInstance(originalReq); } catch (refreshErr) { logout(); return Promise.reject(refreshErr); } } return Promise.reject(err); } ); // 组件卸载时清理拦截器 return () => { axiosInstance.interceptors.request.eject(reqInterceptor); axiosInstance.interceptors.response.eject(resInterceptor); }; }, [checkAuth, logout]); return ( <AuthContext.Provider value={{ user, isLoading, checkAuth, logout }}> {children} </AuthContext.Provider> ); }; // 自定义Hook,简化上下文调用 export const useAuth = () => useContext(AuthContext);
2. 配置Axios实例
创建独立的Axios实例,用于全局请求统一配置:
// axiosInstance.js import axios from 'axios'; const axiosInstance = axios.create({ baseURL: '/api/' // 替换为你的Django API实际地址 }); export default axiosInstance;
3. 实现私有路由守卫
通过路由守卫自动检查认证状态,保护需要登录权限的页面:
// PrivateRoute.jsx import { Navigate, Outlet } from 'react-router-dom'; import { useAuth } from './AuthContext'; const PrivateRoute = () => { const { user, isLoading } = useAuth(); if (isLoading) { return <div>加载中...</div>; // 可替换为自定义加载组件 } // 已认证则渲染目标页面,否则跳转到登录页 return user ? <Outlet /> : <Navigate to="/login" />; }; export default PrivateRoute;
4. 全局注入认证上下文
在App根组件中包裹AuthProvider,确保所有组件都能访问全局认证状态:
// App.jsx import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { AuthProvider } from './AuthContext'; import PrivateRoute from './PrivateRoute'; import Login from './pages/Login'; import Dashboard from './pages/Dashboard'; import Profile from './pages/Profile'; function App() { return ( <AuthProvider> <Router> <Routes> <Route path="/login" element={<Login />} /> {/* 所有需要认证的页面嵌套在PrivateRoute下 */} <Route element={<PrivateRoute />}> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/profile" element={<Profile />} /> </Route> <Route path="/" element={<Navigate to="/dashboard" />} /> </Routes> </Router> </AuthProvider> ); } export default App;
核心优势
- 全局统一管理:认证逻辑集中在AuthContext,无需在每个页面重复编写
getUser函数 - 自动令牌刷新:Axios拦截器自动处理access token过期,业务代码无需关心认证细节
- 路由级保护:私有路由自动拦截未认证用户,避免手动检查状态
- 状态同步:应用启动时自动验证令牌有效性,确保用户状态准确
内容的提问来源于stack exchange,提问作者Tassaras Tassakos
相关产品推荐
相关产品推荐

