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

Next.js+Redux+Firebase Auth:页面渲染前验证用户登录权限方案

解决方案:Next.js + Redux + Firebase Auth 私有页面权限控制

你遇到的核心问题是Firebase Auth的状态初始化是异步操作,当你的AdminLayout组件首次componentDidMount时,Redux store里的currentUser还没从Firebase同步完成,所以拿不到值。下面给你一套完整的解决思路和代码修改方案:

1. 确保Redux与Firebase Auth状态实时同步

首先要保证你的Redux能实时监听Firebase Auth的状态变化,而不是只在页面加载时获取一次。在你的用户action文件里添加一个监听Auth状态的方法:

// redux/user/user.actions.js
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

export const listenToAuthState = () => (dispatch) => {
  // 监听Firebase Auth状态变化
  firebase.auth().onAuthStateChanged(async (authUser) => {
    if (authUser) {
      // 如果用户已登录,从Firestore获取用户的role信息(假设你把角色存在users集合里)
      const userDoc = await firebase.firestore().collection('users').doc(authUser.uid).get();
      const userData = {
        ...authUser,
        role: userDoc.data()?.role || 'user' // 兜底默认角色
      };
      dispatch({ type: 'SET_CURRENT_USER', payload: userData });
    } else {
      dispatch({ type: 'SET_CURRENT_USER', payload: null });
    }
  });
};

然后在你的_app.jsx(Next.js的全局入口)里启动这个监听,确保页面加载时就开始同步Auth状态:

// pages/_app.jsx
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { listenToAuthState } from '../redux/user/user.actions';

function MyApp({ Component, pageProps }) {
  const dispatch = useDispatch();

  useEffect(() => {
    // 页面加载时立即监听Auth状态
    const unsubscribe = dispatch(listenToAuthState());
    // 组件卸载时取消监听
    return () => unsubscribe();
  }, [dispatch]);

  return <Component {...pageProps} />;
}

export default MyApp;

2. 修改AdminLayout,监听currentUser的变化

你的AdminLayout目前只在componentDidMount里检查一次,但此时Auth状态可能还没同步完成。需要添加componentDidUpdate来监听currentUser的变化,确保状态更新后立即校验权限:

// admin-layout.component.jsx
// Imports
class AdminLayout extends React.Component {
  componentDidMount() {
    this.checkUserPermissions();
  }

  componentDidUpdate(prevProps) {
    // 当currentUser状态发生变化时,重新校验权限
    if (this.props.currentUser !== prevProps.currentUser) {
      this.checkUserPermissions();
    }
  }

  checkUserPermissions = () => {
    const { currentUser } = this.props;
    // 未登录用户直接跳转登录页
    if (!currentUser) {
      Router.push("/admin/login");
      return;
    }
    // 已登录但不是admin角色,同样跳转登录(或无权限页面)
    if (currentUser.role !== "admin") {
      Router.push("/admin/login");
      // 可选:跳转到自定义的无权限页面
      // Router.push("/admin/access-denied");
    }
  };

  render() {
    // 可以添加加载状态,避免Auth状态未同步时显示私有内容
    if (!this.props.currentUser) {
      return <div>加载中...</div>;
    }
    // 非admin角色也可以在这里直接不渲染内容
    if (this.props.currentUser.role !== "admin") {
      return null; // 或者显示加载中直到跳转完成
    }

    return (
      <React.Fragment>
        // 你的导航栏等公共组件
        {this.props.children}
        // 其他公共内容
      </React.Fragment>
    );
  }
}

const mapStateToProps = (state) => ({
  currentUser: state.user.currentUser,
});

export default connect(mapStateToProps)(AdminLayout);

3. 可选:路由层面的额外保护(Next.js 12及以下)

如果你想在路由层面直接拦截未授权访问,可以在_app.jsx里全局监听路由变化,校验权限:

// pages/_app.jsx
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useRouter } from 'next/router';
import { listenToAuthState } from '../redux/user/user.actions';

function MyApp({ Component, pageProps }) {
  const dispatch = useDispatch();
  const router = useRouter();
  const currentUser = useSelector(state => state.user.currentUser);

  useEffect(() => {
    const unsubscribe = dispatch(listenToAuthState());
    return () => unsubscribe();
  }, [dispatch]);

  useEffect(() => {
    // 定义需要admin权限的路由
    const protectedRoutes = ['/admin', '/admin/dashboard', '/admin/settings'];
    const isProtectedRoute = protectedRoutes.includes(router.pathname);

    if (isProtectedRoute) {
      if (!currentUser) {
        router.push('/admin/login');
      } else if (currentUser.role !== 'admin') {
        router.push('/admin/login');
      }
    }
  }, [currentUser, router.pathname]);

  return <Component {...pageProps} />;
}

export default MyApp;

关键要点总结

  • Firebase Auth的状态是异步加载的,必须通过onAuthStateChanged监听,不能依赖首次挂载时的同步获取
  • 组件内要监听currentUser的变化,而不是只在挂载时检查一次
  • 可以添加加载状态,避免Auth未同步时泄露私有页面的布局结构

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:07:38