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

参考链接

相关推荐
এ慕ོ冬℘゜1 分钟前
前端实战:jQuery实现动态表格渲染与状态切换功能
前端·javascript·jquery
zzzzzz3105 分钟前
react-bits:把酷炫动效从“灵感截图”变成可复用的 React 组件
javascript·react.js·动效
jay神1 小时前
【计算机毕业设计】基于Flask+Vue的电影数据可视化与智能推荐系统
前端·vue.js·python·信息可视化·flask·毕业设计·课程设计
FreeTinker7 小时前
HTML本地存储技术解析:localStorage与sessionStorage的原理、差异与应用场景
前端·html
qq_452396237 小时前
第十二篇:《全链路监控与可观测性:前端错误追踪与性能分析》
前端
wangruofeng8 小时前
Phosphor Icons 官网源码拆解:URL 即存储、水波动画与 7362 Star 图标库的架构实践
前端·架构·github
BigTopOne8 小时前
WebRTC Native / Android 开发学习资源整理
前端
excel9 小时前
nuxt 3 升级 nuxt 4 报错之 hasInjectionContext
前端
何以解忧,唯有..10 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain