如何在 jwt 验证反应之前阻止路由注册
How to prevent a route from registering before jwt verifies react
今天我遇到了一个问题,在我的 API 验证用户的 JWT 令牌是否有效之前,反应加载了路由。使用 EJS 时,我可以将中间件传递给路由,中间件不会包含 next() 参数。结果,服务器不会呈现 EJS,而这正是我想要通过 React 实现的。也可以让 useNavigate 在导航特定路线时不重新加载吗?
我在 App.js 的路线目前是这样的:
<Route element={<ProtectedRoute access={access}></ProtectedRoute>}>
<Route
path="/login"
exact
element={<Login login={login} access={access}></Login>}
></Route>
<Route
path="/signup"
exact
element={<Signup signup={signup} access={access}></Signup>}
></Route>
<Route
path="/forgot-password"
exact
element={<ForgotPassword access={access}></ForgotPassword>}
></Route>
<Route
path="/reset-password"
exact
element={<ResetPassword access={access}></ResetPassword>}
></Route>
</Route>;
访问函数如下所示:
const access = async (token) => {
return await axios.post(
"http://localhost:5000/access",
{},
{ headers: { Authorization: `Bearer ${token}` } }
);
};
受保护的路由组件如下所示:
import { useState, useContext } from "react";
import { useLocation, useNavigate, Outlet } from "react-router-dom";
import AuthContext from "../Context/AuthProvider";
const ProtectedRoute = ({ access }) => {
const [authorized, setAuthorized] = useState(false);
const { auth } = useContext(AuthContext);
const navigate = useNavigate();
const authorize = async () => {
try {
await access(auth.accessToken);
setAuthorized(true);
} catch (err) {
setAuthorized(false);
}
};
authorize();
if (authorized) {
navigate('/');
} else {
return <Outlet></Outlet>;
}
};
export default ProtectedRoute;
当我使用此代码时,我的登录组件在代码导航回主页之前呈现了一点,我如何使登录组件完全不呈现,只是让它留在主页上?
问题
ProtectedRoute
组件的初始 authorized
状态掩盖了已确认的未验证状态,并且由于组件不等待身份验证确认,它会愉快地错误地重定向到 "/"
。
ProtectedRoute
组件通过 navigate
函数错误地将导航操作作为无意 side-effect 发出,并且在未经身份验证的情况下 return 不是有效的 JSX。请改用 Navigate
组件。
- 如果用户获得授权,
ProtectedRoute
应该呈现 Outlet
以呈现受保护的路由,并且仅在未经授权时重定向到登录。
解决方案
ProtectedRoute
组件应使用不确定的初始 authorized
状态,该状态与经过身份验证的 或 [=60 都不匹配=] 未验证状态,并且 等待 以在呈现 Outlet
或 Navigate
组件之前确认身份验证状态.
示例:
import { useState, useContext } from "react";
import { useLocation, Navigate, Outlet } from "react-router-dom";
import AuthContext from "../Context/AuthProvider";
const ProtectedRoute = ({ access }) => {
const location = useLocation();
const [authorized, setAuthorized] = useState(); // initially undefined!
const { auth } = useContext(AuthContext);
useEffect(() => {
const authorize = async () => {
try {
await access(auth.accessToken);
setAuthorized(true);
} catch (err) {
setAuthorized(false);
}
};
authorize();
}, []);
if (authorized === undefined) {
return null; // or loading indicator/spinner/etc
}
return authorized
? <Outlet />
: <Navigate to="/login" replace state={{ from: location }} />;
};
将登录路由移到ProtectedRoute
布局路由之外。
<Routes>
<Route
path="/login"
element={<Login login={login} access={access} />}
/>
<Route
path="/signup"
element={<Signup signup={signup} access={access} />}
/>
<Route
path="/forgot-password"
element={<ForgotPassword access={access} />}
/>
<Route
path="/reset-password"
element={<ResetPassword access={access} />}
/>
... other unprotected routes ...
<Route element={<ProtectedRoute access={access} />}>
... other protected routes ...
</Route>
</Routes>
保护login/signup/forgot/reset/etc条路线
创建一个 AnonymousRoute
组件,在身份验证状态上反转 Outlet
和 Navigate
组件。这次经过身份验证的用户被重定向到路由之外。
const AnonymousRoute = ({ access }) => {
const [authorized, setAuthorized] = useState(); // initially undefined!
const { auth } = useContext(AuthContext);
useEffect(() => {
const authorize = async () => {
try {
await access(auth.accessToken);
setAuthorized(true);
} catch (err) {
setAuthorized(false);
}
};
authorize();
}, []);
if (authorized === undefined) {
return null; // or loading indicator/spinner/etc
}
return authorized
? <Navigate to="/" replace />
: <Outlet />;
};
...
<Routes>
<Route element={<AnonymousRoute access={access} />}>
<Route path="/login" element={<Login login={login} access={access} />} />
<Route path="/signup" element={<Signup signup={signup} access={access} />} />
<Route path="/forgot-password" element={<ForgotPassword access={access} />} />
<Route path="/reset-password" element={<ResetPassword access={access} />} />
... other protected anonymous routes ...
</Route>
... unprotected routes ...
<Route element={<ProtectedRoute access={access} />}>
... other protected authenticated routes ...
</Route>
</Routes>
今天我遇到了一个问题,在我的 API 验证用户的 JWT 令牌是否有效之前,反应加载了路由。使用 EJS 时,我可以将中间件传递给路由,中间件不会包含 next() 参数。结果,服务器不会呈现 EJS,而这正是我想要通过 React 实现的。也可以让 useNavigate 在导航特定路线时不重新加载吗?
我在 App.js 的路线目前是这样的:
<Route element={<ProtectedRoute access={access}></ProtectedRoute>}>
<Route
path="/login"
exact
element={<Login login={login} access={access}></Login>}
></Route>
<Route
path="/signup"
exact
element={<Signup signup={signup} access={access}></Signup>}
></Route>
<Route
path="/forgot-password"
exact
element={<ForgotPassword access={access}></ForgotPassword>}
></Route>
<Route
path="/reset-password"
exact
element={<ResetPassword access={access}></ResetPassword>}
></Route>
</Route>;
访问函数如下所示:
const access = async (token) => {
return await axios.post(
"http://localhost:5000/access",
{},
{ headers: { Authorization: `Bearer ${token}` } }
);
};
受保护的路由组件如下所示:
import { useState, useContext } from "react";
import { useLocation, useNavigate, Outlet } from "react-router-dom";
import AuthContext from "../Context/AuthProvider";
const ProtectedRoute = ({ access }) => {
const [authorized, setAuthorized] = useState(false);
const { auth } = useContext(AuthContext);
const navigate = useNavigate();
const authorize = async () => {
try {
await access(auth.accessToken);
setAuthorized(true);
} catch (err) {
setAuthorized(false);
}
};
authorize();
if (authorized) {
navigate('/');
} else {
return <Outlet></Outlet>;
}
};
export default ProtectedRoute;
当我使用此代码时,我的登录组件在代码导航回主页之前呈现了一点,我如何使登录组件完全不呈现,只是让它留在主页上?
问题
ProtectedRoute
组件的初始authorized
状态掩盖了已确认的未验证状态,并且由于组件不等待身份验证确认,它会愉快地错误地重定向到"/"
。ProtectedRoute
组件通过navigate
函数错误地将导航操作作为无意 side-effect 发出,并且在未经身份验证的情况下 return 不是有效的 JSX。请改用Navigate
组件。- 如果用户获得授权,
ProtectedRoute
应该呈现Outlet
以呈现受保护的路由,并且仅在未经授权时重定向到登录。
解决方案
ProtectedRoute
组件应使用不确定的初始 authorized
状态,该状态与经过身份验证的 或 [=60 都不匹配=] 未验证状态,并且 等待 以在呈现 Outlet
或 Navigate
组件之前确认身份验证状态.
示例:
import { useState, useContext } from "react";
import { useLocation, Navigate, Outlet } from "react-router-dom";
import AuthContext from "../Context/AuthProvider";
const ProtectedRoute = ({ access }) => {
const location = useLocation();
const [authorized, setAuthorized] = useState(); // initially undefined!
const { auth } = useContext(AuthContext);
useEffect(() => {
const authorize = async () => {
try {
await access(auth.accessToken);
setAuthorized(true);
} catch (err) {
setAuthorized(false);
}
};
authorize();
}, []);
if (authorized === undefined) {
return null; // or loading indicator/spinner/etc
}
return authorized
? <Outlet />
: <Navigate to="/login" replace state={{ from: location }} />;
};
将登录路由移到ProtectedRoute
布局路由之外。
<Routes>
<Route
path="/login"
element={<Login login={login} access={access} />}
/>
<Route
path="/signup"
element={<Signup signup={signup} access={access} />}
/>
<Route
path="/forgot-password"
element={<ForgotPassword access={access} />}
/>
<Route
path="/reset-password"
element={<ResetPassword access={access} />}
/>
... other unprotected routes ...
<Route element={<ProtectedRoute access={access} />}>
... other protected routes ...
</Route>
</Routes>
保护login/signup/forgot/reset/etc条路线
创建一个 AnonymousRoute
组件,在身份验证状态上反转 Outlet
和 Navigate
组件。这次经过身份验证的用户被重定向到路由之外。
const AnonymousRoute = ({ access }) => {
const [authorized, setAuthorized] = useState(); // initially undefined!
const { auth } = useContext(AuthContext);
useEffect(() => {
const authorize = async () => {
try {
await access(auth.accessToken);
setAuthorized(true);
} catch (err) {
setAuthorized(false);
}
};
authorize();
}, []);
if (authorized === undefined) {
return null; // or loading indicator/spinner/etc
}
return authorized
? <Navigate to="/" replace />
: <Outlet />;
};
...
<Routes>
<Route element={<AnonymousRoute access={access} />}>
<Route path="/login" element={<Login login={login} access={access} />} />
<Route path="/signup" element={<Signup signup={signup} access={access} />} />
<Route path="/forgot-password" element={<ForgotPassword access={access} />} />
<Route path="/reset-password" element={<ResetPassword access={access} />} />
... other protected anonymous routes ...
</Route>
... unprotected routes ...
<Route element={<ProtectedRoute access={access} />}>
... other protected authenticated routes ...
</Route>
</Routes>