You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:15:55