在前面幾節,我們已經完成:
下一步就來處理實務上最常見的需求:「角色與權限管理」
例如:
| 使用者角色 | 能進的頁面 | 能做的操作 | 
|---|---|---|
| admin | 所有頁面 | 刪除、修改、審核 | 
| editor | /dashboard, /posts | 編輯但不能刪除 | 
| user | /profile | 只能看自己資料 | 
如果你打算做中大型專案,那麼用 Redux 來集中管理 Auth 狀態與權限會讓架構更清楚。
| 功能 | AuthContext | Redux Toolkit | 
|---|---|---|
| 小專案好上手 | ✅ | ⛔ | 
| 全域共享狀態 | ✅ | ✅ | 
| 可拆 slice / middleware | ⛔ | ✅ | 
| 可支援角色、權限、loading 狀態等複雜管理 | ⛔ | ✅ ✅ ✅ | 
| DevTools 可追蹤狀態變化 | ⛔ | ✅ | 
| 適合大型專案 | ❌ | ✅ ✅ ✅ | 
建立 authSlice
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { loginAndSetCookie, logout, getProtectedData } from "../utils/auth";
// 異步登入
export const loginThunk = createAsyncThunk(
  "auth/login",
  async ({ email, password }) => {
    const data = await loginAndSetCookie(email, password);
    return data; // { uid, email }
  }
);
// 驗證當前使用者(從 Cookie)
export const fetchCurrentUser = createAsyncThunk(
  "auth/me",
  async () => {
    const data = await getProtectedData(); // 後端從 Cookie 驗證
    return data.user; // { uid, email, role }
  }
);
const authSlice = createSlice({
  name: "auth",
  initialState: {
    user: null,
    role: null, // admin / editor / user ...
    loading: false,
  },
  reducers: {
    clearAuth: (state) => {
      state.user = null;
      state.role = null;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(loginThunk.pending, (state) => {
        state.loading = true;
      })
      .addCase(loginThunk.fulfilled, (state, action) => {
        state.loading = false;
        state.user = action.payload;
        state.role = action.payload.role || "user";
      })
      .addCase(fetchCurrentUser.fulfilled, (state, action) => {
        state.user = action.payload;
        state.role = action.payload.role || "user";
      });
  },
});
export const { clearAuth } = authSlice.actions;
export default authSlice.reducer;
建立 Redux Store
import { configureStore } from "@reduxjs/toolkit";
import authReducer from "./authSlice";
export const store = configureStore({
  reducer: {
    auth: authReducer,
  },
});
在 App 最外層包住 Provider
import { Provider } from "react-redux";
import { store } from "./store";
ReactDOM.createRoot(document.getElementById("root")).render(
  <Provider store={store}>
    <AppRoutes />
  </Provider>
);
ProtectedRoute 加上「角色判斷」
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
export default function ProtectedRoute({ children, roles = [] }) {
  const { user, role, loading } = useSelector((state) => state.auth);
  if (loading) return <p>驗證中...</p>;
  if (!user) return <Navigate to="/login" replace />;
  if (roles.length > 0 && !roles.includes(role)) {
    return <p>你沒有權限(需要: {roles.join(", ")})</p>;
  }
  return children;
}
配置不同權限的路由
<Route
  path="/admin"
  element={
    <ProtectedRoute roles={["admin"]}>
      <AdminPanel />
    </ProtectedRoute>
  }
/>
<Route
  path="/editor"
  element={
    <ProtectedRoute roles={["admin", "editor"]}>
      <EditorPage />
    </ProtectedRoute>
  }
/>