如何在 Next.js 中实现受保护的路由
单页应用程序在身份验证或安全性方面必不可少的功能之一是能够根据用户的身份验证状态有条件地向其显示某些用户界面。
在本文中,您将学习如何在 Next.js 应用程序中实现此功能,因为您不希望未经授权的用户访问仪表板等私有用户界面,除非他们当前已通过身份验证。
免责声明:我最近发布了一篇关于如何在 Next.js 中使用
getServerSidePropsCookie进行身份验证的指南。它还涉及如何在服务器端实现受保护的路由,而无需使用受保护资源的闪烁效果。如果您最终按照本指南操作,您可能还想查看另一篇指南,该指南将引导您完成持久化身份验证状态和选择合适的 Cookie 包装器的过程。
但是,在您继续阅读本文之前,您应该对以下内容有所了解:
- React中的条件渲染
- localStorage 及其基本方法
- React Context API 的基础知识
设置 Next.js 应用
本文将重点介绍如何使用 Next.js。让我们通过在终端中输入以下命令来创建一个 Next.js 应用。
npx create-next-app [name-of-your-app]
让我们来看一下下面应用程序的文件结构。我们将重点介绍此应用程序所需的重要文件,因此内容会比较简洁。
|--pages
| |-- api
| |-- _app.js
| |-- index.js (dashboard page)
|--src
| |-- context
| | |-- auth-context.js
| |__
|__
pages 目录是应用程序所有路由操作发生的地方。这是 Next.js 的一个开箱即用功能。它省去了您编写独立路由的麻烦。
pages/_app.js这里是所有组件连接到 DOM 的地方。如果你查看组件结构,你会发现所有组件也都作为 pageProps 传递给了 Component props。
npm run dev
设置身份验证上下文
在上一节中,我们了解了 Next.js 应用程序的基本结构以及本文中我们将要交互的文件的功能。
首先,context我们进入存放该auth-context.js文件的文件夹。这个文件借助 React 的 Context API,帮助我们存储应用程序的身份验证状态。如果您还不熟悉Context API,可以点击此处了解更多信息。
// src/context/auth-context.js
import React from "react";
import { useRouter } from "next/router";
const AuthContext = React.createContext();
const { Provider } = AuthContext;
const AuthProvider = ({ children }) => {
const [authState, setAuthState] = React.useState({
token: "",
});
const setUserAuthInfo = ({ data }) => {
const token = localStorage.setItem("token", data.data);
setAuthState({
token,
});
};
// checks if the user is authenticated or not
const isUserAuthenticated = () => {
if (!authState.token) {
return false;
}
};
return (
<Provider
value={{
authState,
setAuthState: (userAuthInfo) => setUserAuthInfo(userAuthInfo),
isUserAuthenticated,
}}
>
{children}
</Provider>
);
};
export { AuthContext, AuthProvider };
上面的代码片段包含了我们在应用程序中保持身份验证状态所需的一切。但是,让我们将其分解成更小的部分,并了解它的作用。
你会注意到,我们在 React 中使用了useStatehook 来定义身份验证状态的初始值authState,并将数据类型设置为对象。
const [authState, setAuthState] = React.useState({
token: "",
});
你可能会问,为什么?这样做是为了方便我们在应用程序中处理多个状态。例如,假设除了用户的身份验证状态之外,我们还需要保留其他状态,我们只需要向对象添加另一个属性即可authState。
现在,我们需要一种方法来正确存储分配给每个用户的唯一 JWT(JSON Web Token),该 JWT 是用户首次注册我们的应用时获得的。这就需要用到浏览器的 localStorage API。
const setUserAuthInfo = ({ data }) => {
const token = localStorage.setItem("token", data.data);
setAuthState({
token,
});
};
setAuthState我们在上面的代码片段中所做的,是将用户令牌存储在 localStorage 中,并寻找一种方法,通过使用我们在 useState hook 中声明的 setter 函数,使令牌的值在应用程序状态中可用。
到目前为止,我们所做的只是存储用户信息(即令牌)。下一步是在localStorage页面首次加载时检查浏览器缓存中是否存在 JWToken。
const isUserAuthenticated = () => {
if (!authState.token) {
return false;
}
};
上面的代码片段似乎不合适,因为我已经在该代码块中否定了条件,所以该isUserAuthenticated函数不会返回。trueif
Casey Choiniere建议进行以下更改——以及在useEffect用户未通过身份验证时将用户重定向回主页的钩子中。
const isUserAuthenticated = () => !!authState.token;
上面的代码片段会检查令牌是否存在。如果令牌不在 localStorage 中,则返回 false;如果令牌在 localStorage 中,则返回 true。
这些函数反过来又作为值传递给valueprop。Provider
<Provider
value={{
authState,
setAuthState: (userAuthInfo) =>
setUserAuthInfo(userAuthInfo),
isUserAuthenticated,
}}
/>
在仪表盘页面中使用 authContext
现在可以将上一节中的 authContext 导入到仪表板页面中,并且我们可以使用ProviderisUserAuthenticated中的 propauthContext来检查用户是否已经通过身份验证。
// pages/dashboard
export default function Dashboard() {
const router = useRouter();
const authContext = React.useContext(AuthContext);
React.useEffect(() => {
// checks if the user is authenticated
authContext.isUserAuthenticated()
? router.push("/dashboard")
: router.push("/");
}, []);
return (
<React.Fragment>
<Head>
<title>Dashboard</title>
</Head>
<div>
<h2>Dashboard</h2>
</div>
</React.Fragment>
);
}
为了实现这一点,条件语句必须放在useEffectReact.js 的 hook 中。因为 hook 会在组件(即我们的仪表盘页面)的每次新渲染时运行。
因此,每当用户手动访问仪表盘页面而未先登录时,都会被重定向回主页或登录页面。
React.useEffect(() => {
// checks if the user is authenticated
authContext.isUserAuthenticated()
? router.push("/")
: router.push("/dashboard");
}, []);
在上面的代码片段中,您可以看到我们使用了 Next.js 的 useRouter 模块来访问应用程序的路由。请记住,isUserAuthenticated在 authContext 中,该函数始终返回 false。
所以现在,在这种情况下,如果令牌不在 localStorage 中,用户将始终被发送到登录路由或至少是主页。
结论
如果你的应用程序有很多路由,你不希望未经身份验证的用户能够访问这些路由,你只需要在这些单独的路由中重复该过程即可。
感谢您阅读本文,希望它能帮助您了解如何在 Next.js 中实现受保护的路由。
文章来源:https://dev.to/seven/how-to-implement-protected-routes-in-nextjs-1m50