我正在嘗試使用AWS Amplify建立一個網路應用程式。我已經配置了身份驗證,但我希望某些頁面僅供經過身份驗證的用戶使用,例如任何人都可以看到主頁,但只有登入的用戶才能看到「/dashboard」。我目前正在使用AWS Amplify作為我的後端,並使用React前端,使用react-router v6在頁面之間進行路由。
目前,我的路由程式碼非常簡單(這是我第一次使用React),並且位於App.js中:
import React from 'react'; import { BrowserRouter, Route, Routes, } from 'react-router-dom'; import Login from './pages/Login'; import Home from './pages/Home'; import Dashboard from './pages/Dashboard'; import ErrorPage from './pages/ErrorPage'; const App = () => { return ( <BrowserRouter> <Routes> <Route exact path="/" element={<Home />} /> <Route path="/login" element={<Login />} /> <Route path="/dashboard" element={<Dashboard />} /> <Route path="*" element={<ErrorPage />} /> </Routes> </BrowserRouter> ); } export default App;
我先嘗試使用withAuthenticator
包裝我想要進行驗證的頁面,但這只會導致循環顯示登入框。
function Dashboard({ signOut, user }) { return ( <> <h1>Hello {user.username}, this is still in development.</h1> <button onClick={signOut}> Sign out</button> </> ); } export default withAuthenticator(Dashboard);
我還嘗試添加一個函數來檢查用戶是否經過身份驗證,並返回不同的內容,但這只會對經過身份驗證和未經身份驗證的用戶顯示空白畫面。我認為這是因為它是async
,但我對React不夠熟悉,無法理解為什麼以及如何修復它。
async function isAuthed() { try { await Auth.currentAuthenticatedUser(); return true; } catch(e) { return false; } } async function Dashboard() { if (await isAuthed()) { return ( <> <h1>Hello, this is still in development.</h1> </> ); } else { return ( <> <h1>Please login to view this page.</h1> </> ) } }
我還嘗試查看是否有一些非同步路由的方法,但不確定如何實現。
編輯:
@Jlove的解決方案已經如預期運作,我更新後的App.js
路由程式碼如下:
import React, { useState, useEffect } from 'react'; import { BrowserRouter, Route, Routes, useNavigate, } from 'react-router-dom'; import { Amplify, Auth } from 'aws-amplify' import Login from './pages/Login'; import Home from './pages/Home'; import Dashboard from './pages/Dashboard'; import ErrorPage from './pages/ErrorPage'; import Unauthenticated from './pages/Unauthenticated'; function RequireAuth({ children }) { const navigate = useNavigate(); const [isAuth, setIsAuth] = useState(null); useEffect(() => { Auth.currentAuthenticatedUser() .then(() => setIsAuth(true)) .catch(() => { navigate("/unauthenticated") }) }, []) return isAuth && children; } const App = () => { return ( <BrowserRouter> <Routes> <Route exact path="/" element={<Home />} /> <Route path="/login" element={<Login />} /> <Route path="/dashboard" element={ <RequireAuth> <Dashboard /> </RequireAuth> } /> <Route path="*" element={<ErrorPage />} /> <Route path="/unauthenticated" element={<Unauthenticated />} /> </Routes> </BrowserRouter> ); } export default App;
P粉7258276862023-09-10 10:13:10
您將希望將保護路由的邏輯與每個路由渲染的內容分開。不要將身份驗證與您要在路由上呈現的UI/內容元件混合在一起。
常見的保護模式是使用佈局路由來包裝您想要保護存取權限的整個路由群組。您將建立一個佈局路由元件,觸發一個效果來檢查目前使用者的身份驗證狀態,並有條件地傳回:
Outlet
(如果使用者已經通過身份驗證)這樣可以防止(a)在知道用戶未經身份驗證之前意外訪問受保護的頁面,以及(b)在知道用戶已經通過身份驗證之前意外重定向到登入頁面。
範例:
const checkAuthentication = async () => {
try {
await Auth.currentAuthenticatedUser();
return true;
} catch {
return false;
}
};
import { Outlet, Navigate } from 'react-router-dom'; const ProtectedRoute = () => { const [isAuth, setIsAuth] = useState(undefined); useEffect(() => { checkAuthentication() .then(() => setIsAuth(true)) .catch(() => setIsAuth(false)); }, []); if (isAuth === undefined) { return null; // 或加载中的旋转器/指示器等 } return isAuth ? <Outlet /> : <Navigate to="/login" replace />; }
包裝需要受保護的路由。
import React from 'react'; import { BrowserRouter, Route, Routes, } from 'react-router-dom'; import Login from './pages/Login'; import Home from './pages/Home'; import Dashboard from './pages/Dashboard'; import ErrorPage from './pages/ErrorPage'; import ProtectedRoute from './components/ProtectedRoute'; const App = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="/login" element={<Login />} /> <Route element={<ProtectedRoute />}> <Route path="/dashboard" element={<Dashboard />} /> {/* ... 其他受保护的路由 ... */} </Route> <Route path="*" element={<ErrorPage />} /> </Routes> </BrowserRouter> ); } export default App;
P粉9785510812023-09-10 09:28:28
以下是一種方法,透過將元件路由包裝在授權元件中來實現:
<Route path="/somePathToProtect" element={ <RequireAuth> <Dashboard /> </RequireAuth> } /> export function RequireAuth({children}) { const navigate = useNavigate(); const [isAuth, setIsAuth] = useState(null); useEffect(() => { Auth.currentAuthenticatedUser() .then( () => setIsAuth(true) ) .catch(() => { navigate('/routeToCatchNonAuth') }) }, []) return isAuth && children; }
這裡的目標是基於Auth
傳回的結果來保護您的路由。如果Auth
選擇了catch路由,利用路由器將使用者導航到未經授權的頁面。