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

Firebase、Router与ContextAPI下手动URL路径访问权限问题

问题描述
  • 需求:已登录用户可通过浏览器手动输入URL访问私有路由,未登录用户自动重定向至登录页;登录(/sign-in)、注册(/sign-up)页面允许公开访问。
  • 当前问题:无论是否登录,手动访问任何私有路由都会被重定向到登录页。
问题根源
  1. 路由结构不合理:所有路由(包括登录、注册)都被App组件中的PrivateRoute包裹,导致公开路由也会触发登录校验,甚至可能出现循环重定向;同时,私有路由的保护逻辑没有区分公开/私有路由范围。
  2. 登录状态校验逻辑有漏洞:PrivateRoute中使用currentUser?.email判断登录状态,但Firebase用户对象存在时即表示已登录,依赖email可能因用户登录方式(如第三方登录)导致误判;另外,未处理Auth状态加载中的情况,可能在状态未初始化时就触发重定向。
  3. Context状态更新的调试误导:ContextProvider中useEffect的console.log(currentUser)在setCurrentUser之后,但因React setState异步特性,无法打印最新用户状态,容易干扰问题排查。
解决方案

1. 拆分路由结构,分离公开与私有路由

将登录、注册这类公开路由从App的子路由中移出,单独作为顶层路由,避免被PrivateRoute校验:

const Main = () => {
  const router = createBrowserRouter(
    [
      // 公开路由:直接挂载,无需经过PrivateRoute
      {
        path: "/sign-in",
        element: <ContextProvider><LogIn /></ContextProvider>,
      },
      {
        path: "/sign-up",
        element: <ContextProvider><Register /></ContextProvider>,
      },
      // 私有路由:由App组件统一包裹PrivateRoute保护
      {
        path: "/",
        element: <App />,
        children: [
          { path: "/", element: <GroupMenu /> },
          { path: "/group-content", element: <GroupContent /> },
          { path: "/dashboard", element: <Dashboard /> },
        ],
      },
    ],
    { basename: "/thatswhy_items_counter/" }
  );

  return (
    <React.StrictMode>
      <ThemeProvider theme={theme}>
        <RouterProvider router={router} />
      </ThemeProvider>
    </React.StrictMode>
  );
};

2. 完善PrivateRoute的登录校验逻辑

添加加载状态判断,避免在Auth状态未初始化时误重定向;同时直接判断用户对象是否存在,替代依赖email的校验:

const PrivateRoute = ({ children }: { children: React.ReactNode }) => {
  const currentUser = useContextSelector(ContextAPI, (v) => v?.currentUser);
  const loading = useContextSelector(ContextAPI, (v) => v?.loading);

  // 加载状态时显示占位内容,避免过早触发重定向
  if (loading) {
    return <div>加载中...</div>; // 可替换为项目中的加载组件
  }

  return currentUser ? children : <Navigate to="/sign-in" />;
};

3. 修正Context中的Auth状态监听逻辑

调整onAuthStateChanged回调中的日志位置,确保打印最新用户状态;简化状态更新逻辑:

const ContextProvider = ({ children }: { children: React.ReactNode }) => {
  const [currentUser, setCurrentUser] = useState<firebase.User | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const { signUpValues, SignUpInputConstructor, handleRegister, signUpError } = useAuth();
  const { logInValues, SignInInputConstructor, handleLogIn, logInError } = useLogIn();
  const { handleLogout, logOutError } = useLogOut();

  const vals: TContextAPI = {
    handleLogout,
    logOutError,
    setLoading,
    currentUser,
    signUpValues,
    SignUpInputConstructor,
    handleRegister,
    loading,
    signUpError,
    logInValues,
    SignInInputConstructor,
    handleLogIn,
    logInError,
  };

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setCurrentUser(user);
      setLoading(false);
      console.log("当前登录用户:", user); // 此处打印可获取最新用户状态
    });
    return unsubscribe;
  }, []);

  return <ContextAPI.Provider value={vals}> {!loading && children} </ContextAPI.Provider>;
};

4. 保持App组件的私有路由保护逻辑

App组件中的PrivateRoute继续包裹Outlet,确保所有私有子路由都经过登录校验:

function App() {
  return (
    <ContextProvider>
      <h1>Hello World!</h1>
      <Button variant="contained">Something</Button>
      <PrivateRoute>
        <Outlet />
      </PrivateRoute>
    </ContextProvider>
  );
}
验证步骤
  1. 未登录状态下,手动输入私有路由(如/dashboard),应自动跳转到/sign-in;
  2. 登录成功后,手动输入私有路由,应正常进入对应页面;
  3. 无论登录状态如何,手动输入/sign-in或/sign-up,都能直接访问对应页面。

内容的提问来源于stack exchange,提问作者dimonchik11

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 09:44:53