React Router 7 全局路由保护

之前项目的路由保护是这样的:每个需要登录的页面都判断一下 token,没有就跳转登录页。

javascript 复制代码
// 之前:每个页面都要写
const SomePage = () => {
  const token = localStorage.getItem('token');
  if (!token) {
    return <Navigate to="/login" />;
  }
  
  return <div>页面内容</div>;
};

这样写有几个问题:

  1. 每个页面都要重复这个逻辑
  2. 万一漏了一个页面,就有安全漏洞
  3. 代码很乱,业务逻辑混在一起

后来我改成全局路由保护,清爽多了。

React Router 7 的新特性

React Router 7 推出了 <BrowserRouter>basename 和一些新特性,但最实用的是嵌套路由。

但我的方案不是在每个路由外面套 <ProtectedRoute>,而是在 main.jsx 里套一个全局的:

javascript 复制代码
// main.jsx
<BrowserRouter>
  <ProtectedRoute>  {/* 全局保护,只套一次 */}
    <ConfigProvider>
      <App />
    </ConfigProvider>
  </ProtectedRoute>
</BrowserRouter>

这样 <App /> 里面的所有路由都会经过保护逻辑。

ProtectedRoute 的实现

ProtectedRoute 组件要做三件事:

  1. 检查 token,没有就跳登录
  2. 检查路由是否合法(防止访问不存在的页面)
  3. 处理登录后跳回原页面的逻辑
javascript 复制代码
const ProtectedRoute = ({ children }) => {
  const location = useLocation();
  const pathname = location.pathname;
  
  // 1. 检查路由是否合法
  const allowedRoutes = [
    "/login", "/", "/chat", "/customer", "/review-dashboard", "/pdf-annotator/:id", ...
  ];
  
  const isValidRoute = allowedRoutes.some(route =>
    pathname === route || pathname.startsWith(`${route}/`)
  );
  
  if (!isValidRoute) {
    return <Navigate to="/404" replace />;
  }
  
  // 2. 登录页面直接放行
  if (pathname === "/login") {
    return <>{children}</>;
  }
  
  // 3. 检查 token
  const token = localStorage.getItem("token");
  if (!token) {
    localStorage.setItem("pathname", pathname); // 保存原路径
    return <Navigate to="/login" replace />;
  }
  
  // 4. 根路径重定向
  if (pathname === "/") {
    return <Navigate to="/chat" replace />;
  }
  
  return <>{children}</>;
};

这样写的好处:

  • 所有路由都在一个地方管理,不会漏
  • 未定义的路由自动跳 404
  • 登录后自动跳回原页面
但是有个坑:401 错误处理

用户登录后,token 会过期。这时候后端返回 401,我需要自动跳转到登录页。

但这个逻辑不能写在 ProtectedRoute 里,因为它只在路由切换时执行,不会响应 API 请求。

我把它写在了 Axios 拦截器里:

javascript 复制代码
// request.js
export let isRelogin = { show: false };

request.interceptors.response.use(
  (response) => {
    if (response.data.code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        message.error('登录状态已过期');
        localStorage.setItem("pathname", window.location.pathname);
        localStorage.removeItem("token");
        window.location.href = "/login";
      }
    } else if (response.data.code !== 200) {
      message.error(response.data.msg);
      return Promise.reject(response.data);
    }
    return response;
  },
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem("token");
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);

这里有个坑:如果多个请求同时返回 401,会弹出多次错误提示。

我用了个 isRelogin.show 标志位,确保只弹一次:

javascript 复制代码
export let isRelogin = { show: false };

if (response.data.code === 401) {
  if (!isRelogin.show) {  // 只处理第一次
    isRelogin.show = true;
    message.error('登录状态已过期');
    window.location.href = "/login";
  }
}

登录成功后,记得重置这个标志位:

javascript 复制代码
// 登录成功后
isRelogin.show = false;
懒加载怎么处理?

React Router 7 推荐用懒加载,但 ProtectedRoute 会阻止懒加载的组件渲染。

我的方案是:只懒加载页面组件,不懒加载 ProtectedRoute

javascript 复制代码
// App.jsx
const HomePage = lazy(() => import("./view/HomePage"));
const PDFAnnotatorDemo = lazy(() => import("./view/PDFAnnotatorDemo"));

const App = () => {
  return (
    <Suspense fallback={<PageLoading />}>
      <Routes>
        <Route path="/homepage" element={<HomePage />} />
        <Route path="/pdf-annotator/:id" element={<PDFAnnotatorDemo />} />
        ...
      </Routes>
    </Suspense>
  );
};

ProtectedRoutemain.jsx 里,不会被懒加载,所以一开始就会加载。

最后的效果

现在的路由架构:

  • main.jsx:全局保护 + 登录页不懒加载
  • App.jsx:懒加载所有其他页面
  • request.js:401 自动跳登录

代码清爽多了,也不用担心漏保护某个页面。

几个踩坑总结
  1. 全局保护比单独保护好 :一次套在 main.jsx 里就行
  2. 401 处理要防重复 :用 isRelogin.show 标志位
  3. 路由白名单要维护:未定义的路由跳 404
  4. 懒加载不能保护 ProtectedRoute:它要最先加载
  5. 登录后要跳回原路径 :用 localStorage.pathname 保存
相关推荐
起风的蛋挞2 小时前
Matlab提示词语法
前端·javascript·matlab
有味道的男人2 小时前
1688获得商品类目调取商品榜单
java·前端·spring
txwtech2 小时前
第20篇esp32s3小智设置横屏
前端·html
Exquisite.2 小时前
企业高性能web服务器---Nginx(2)
服务器·前端·nginx
DFT计算杂谈2 小时前
VASP+PHONOPY+pypolymlpj计算不同温度下声子谱,附批处理脚本
java·前端·数据库·人工智能·python
广州华水科技2 小时前
如何选择合适的单北斗变形监测系统来保障水库安全?
前端
Mr_Xuhhh2 小时前
MySQL表的内连接与外连接详解
java·前端·数据库
Amumu121382 小时前
Vue Router(一)
前端·javascript·vue.js
郑州光合科技余经理2 小时前
可独立部署的Java同城O2O系统架构:技术落地
java·开发语言·前端·后端·小程序·系统架构·uni-app