我正在尝试使用AWS Amplify构建一个Web应用程序。我已经配置了身份验证,但我希望某些页面仅供经过身份验证的用户使用,例如任何人都可以看到主页,但只有登录的用户才能看到“/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路由,利用路由器将用户导航到未经授权的页面。