如何实现 React Router 的路由鉴权

React RouterReact 应用中最常用的路由库之一,路由鉴权是确保应用安全性和数据保护的关键部分。 本文将介绍如何使用 React Router 实现路由鉴权,以及如何限制用户访问特定页面,确保只有经过鉴权的用户可以访问(如登录后、购买会员后等条件)。

以官方文档中的 Auth Example 代码为例, 在线演示可以见 StackBlitz

1. 定义路由

这里我们定义三个路由,分别为 首页 /,登录页 /login,需要登录后才能访问的页面 /protected

tsx 复制代码
export default function App() {
  return (
      <Routes>
        <Route element={<Layout />}>
          <Route path="/" element={<PublicPage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route
            path="/protected"
            element={<ProtectedPage />}
          />
        </Route>
      </Routes>
  );
}

function LoginPage() {
  let navigate = useNavigate();
  let location = useLocation();

  let from = location.state?.from?.pathname || "/";

  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();

    let formData = new FormData(event.currentTarget);
    let username = formData.get("username") as string;

    signin(username, () => {
      navigate(from, { replace: true });
    });
  }

  return (
    <div>
      <p>You must log in to view the page at {from}</p>

      <form onSubmit={handleSubmit}>
        <label>
          Username: <input name="username" type="text" />
        </label>{" "}
        <button type="submit">Login</button>
      </form>
    </div>
  );
}

function PublicPage() {
  return <h3>Public</h3>;
}

function ProtectedPage() {
  return <h3>Protected</h3>;
}

2. 实现路由拦截组件

实现路由拦截组件只需要判断用户是否登录即可,也可以按照项目实际需求判断条件。这个组件的作用是用来包裹需要鉴权的路由组件,如果不符合判断条件则跳转到登录页面。

tsx 复制代码
function RequireAuth({ children }: { children: JSX.Element }) {
  let auth = useAuth();
  let location = useLocation();
  
  // 这里判断用户是否登录,如果未登录则跳转到登录页面
  if (!auth.user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return children;
}

判断用户登录官方示例代码中是使用 Context 这个 React 特性实现的 AuthProvider ,便于不同组件获取共用的状态。 我们也可以使用其他符合项目的方式判断用户是否登录,比如将用户登录状态存到全局状态库(如 Redux)或者 localStorage 中,示例代码如下:

tsx 复制代码
const isLogin = useAppSelector((state) => state.user.isLogin);
const userInfo = useAppSelector((state) => state.user.userInfo);
const dispatch = useAppDispatch();

useEffect(() => {
  if (isLogin && !userInfo) {
    dispatch(fetchUserInfo());
  }
}, [dispatch, isLogin, userInfo]);

if (!isLogin) {
  return <Navigate to="/login" state={{ from: location }} replace />;
}

3. 为需要鉴权的路由增加路由拦截组件包裹

最后只需要为需要鉴权的路由增加路由拦截组件即可,这么实现充分的利用的 React 的组合式思想,可以方便的为是否需要鉴权的路由增减鉴权功能(便于配置维护), 也通过封装统一的组件提高了代码的复用,逻辑改变时只需要修改通用组件即可。

tsx 复制代码
export default function App() {
  return (
      <Routes>
        <Route element={<Layout />}>
          <Route path="/" element={<PublicPage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route
            path="/protected"
            element={
              <RequireAuth>
                <ProtectedPage />
              </RequireAuth>
            }
          />
        </Route>
      </Routes>
  );
}

参考链接

相关推荐
顾安r5 小时前
11.8 脚本网页 星际逃生
c语言·前端·javascript·flask
Hello.Reader5 小时前
Data Sink定义、参数与可落地示例
java·前端·网络
im_AMBER6 小时前
React 17
前端·javascript·笔记·学习·react.js·前端框架
谷歌开发者7 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (六)
前端·chrome·学习
一晌小贪欢7 小时前
【Html模板】电商运营可视化大屏模板 Excel存储 + 一键导出(已上线-可预览)
前端·数据分析·html·excel·数据看板·电商大屏·大屏看板
发现你走远了7 小时前
连接模拟器网页进行h5的调试(使用Chrome远程调试(推荐)) 保姆级图文
前端·chrome
街尾杂货店&8 小时前
css - 实现三角形 div 容器,用css画一个三角形(提供示例源码)简单粗暴几行代码搞定!
前端·css
顺凡8 小时前
删一个却少俩:Antd Tag 多节点同时消失的原因
前端·javascript·面试
小白路过8 小时前
CSS transform矩阵变换全面解析
前端·css·矩阵
爬山算法8 小时前
Redis(110)Redis的发布订阅机制如何使用?
前端·redis·bootstrap