Next.js 开发入门之保护路由

739 阅读7分钟

Web 应用程序为了保护用户和数据安全,确保只有经过身份验证的用户才能访问特定路由是至关重要的。因此,如何对路由进行保护是 Web 开发不可忽视的问题。

在本教程中,我们将深入探讨三种不同方法是如何在 Next.js 13 中实现路由保护的。

介绍

路由保护是 Web 应用程序开发的一个重要方面,特别是在用户管理个人数据时,其重要性尤为凸显。

我们需要根据用户的身份认证状态来控制他们对特定路由的访问权限。例如,确保未认证的用户无法访问管理面板或查看其他用户的敏感信息。

在开始这段学习之旅之前,请确保你具备以下先决条件:

  • 对 JavaScript、Node.js 和 Next.js 有基本了解
  • 熟悉 NPM 或 Yarn 等包管理工具的使用
  • 选择一款适合前端开发的代码编辑器(例如 Visual Studio Code)
  • 在你的机器上安装 Node.js 和 npm(或 yarn)

让我们先来介绍一下 Next.js 是什么。根据其官方文档,Next.js 是一个基于 React 的全栈 Web 应用框架。它提供了一系列功能,包括基于文件的路由系统、客户端和服务器端渲染能力、图像优化,以及对 TypeScript 的深度支持,使其成为构建现代 Web 应用程序的强大工具。

在开始之初,你可以使用以下命令来创建一个新的 Next.js 项目:

npx create-next-app@latest

在安装过程中,你将收到许多配置提示。我建议选择 app 路由,因为这是 Next.js 13 推荐使用的路由系统。在本教程中,我们将使用 Tailwind CSS 来处理样式设计,并采用 TypeScript 作为编程语言。

Next.js 的路由系统

与通常依赖 react-router-dom 这样的第三方库来处理路由的 React 应用程序不同,Next.js 13 引入了一个全新的内置app路由系统。这是一种基于文件的路由系统。这种设计允许开发者通过文件夹结构来定义路由,从而简化路由配置。此外,这种结构还支持嵌套路由,意味着一个文件夹内可以包含一个或多个子文件夹。

在讨论如何在 Next.js 中保护路由之前,我们先来了解一下如何创建路由。

如何在 Next.js 中创建路由

为了展示在 Next.js 13 中如何实现路由保护,我们将构建以下页面(路由):首页、仪表板、管理员页面、设置页面。

首先让我们删除app文件夹中page.tsx文件中的所有代码,并插入以下代码:

// app/page.tsx

<main className="text-center h-screen flex justify-center items-center">
    <p>Home page</p>
</main>

app文件夹中的page.tsx文件将作为本教程的首页。在app文件夹中,创建profile文件夹,并在其中创建page.tsx文件。添加以下代码:

// profile/page.tsx

<main className="text-center h-screen flex justify-center items-center">
    <div>
        <h1>Profile page</h1>        
    </div>
</main>

重复此步骤创建dashboard、admin和settings文件夹,每个文件夹都包含一个page.tsx文件。

完成如上所述,我们开始讨论本教程的正文——如何保护路由。

客户端组件

在 Next.js 中,客户端组件是一种在客户端浏览器中进行实时渲染的 React 组件,它能够充分利用客户端 JavaScript 实现更丰富的交互体验。

为了防止未经身份验证的用户访问特定客户端组件页面,必须实施访问限制,下面,我们将通过示例来演示如何实现客户端组件保护机制。

为了保持教程的简单性,我们将不涉及身份验证的具体实现。取而代之的是,我们将通过创建一个变量来模拟认证状态。

请在 Next.js 应用的根目录下的 Utils 文件夹内创建一个名为 Auth.ts 的文件。该文件应包含以下代码:

export const isAuthenticated = false;

我们将对 profile 页面实施客户端路由保护,确保只有经过身份验证的用户才能访问。以下是示例代码:

// profile/page.tsx

"use client";
import {isAuthenticated} from '@/Utils/Auth';
import { redirect } from 'next/navigation';
import { useLayoutEffect } from 'react';

const Profile = () => {

    useLayoutEffect(() => {
        const isAuth = isAuthenticated;
        if(!isAuth){
            redirect("/")
        }
    }, [])
   
    return (
        <main className="text-center h-screen flex justify-center items-center">
            <div>
                <h1>Profile</h1>        
            </div>
        </main>
    );
};

export default Profile;

Profile组件中,我们通过使用useLayoutEffect钩子实现了一种基本的路由保护机制。useLayoutEffect 钩子在挂载组件时检查用户的身份验证状态,如果用户未经身份验证,则调用redirect函数将用户重定向到首页。

当然,我们还可以通过使用高阶组件(HOC)重构代码来简化 Profile 路由的保护,这是一种在客户端实现路由保护的更清晰方式。

为了使用高阶组件,我们需要在 components 目录下新建一个名为 isAuth 的文件,并写入以下代码:

// isAuth.tsx

"use client";
import { isAuthenticated } from "@/Utils/Auth";
import { useEffect } from "react";
import { redirect } from "next/navigation";

export default function isAuth(Component: any) {
    return function IsAuth(props: any) {
        const auth = isAuthenticated;

        useEffect(() => {
            if (!auth) {
                return redirect("/");
            }
        }, []);

        if (!auth) {
            return null;
        }

        return <Component {...props} />;
    };
}

上面的代码定义了一个高阶组件 (isAuth),用于检查用户的身份验证状态。如果用户未经过身份验证,则会阻止呈现受保护的组件并将其重定向到主页。

这个高阶组件将用于保护我们应用程序中的 Dashboard 页面,具体使用方法代码如下:

// dashboard/page.tsx

import isAuth from "@/Components/isAuth";

const Dashboard = () => {
    return (
        <main className=" h-screen flex justify-center items-center">
            <p>Dashboard</p>
        </main>
    );
};

export default isAuth(Dashboard);

上面的代码通过使用 isAuth 高阶组件包装 Dashboard 组件,确保了只有身份验证的用户才能访问 Dashboard 页面。

至此,我们已成功学会利用 useLayoutEffect 和高阶组件(HOC)来保护客户端组件。

服务器组件

在 Next.js 中,所有组件默认为服务器组件。如何保护服务器组件防止未经身份验证的用户访问也是不可回避的话题。其实保护服务器组件的方法相当直接,示例代码如下:

// admin/page.tsx

import {isAuthenticated} from '@/Utils/Auth';
import { redirect } from 'next/navigation';

const Admin = () => {
    const isAuth = isAuthenticated;

    if(!isAuth) {
        redirect("/");
    }
    return (
        <main className="text-center h-screen flex justify-center items-center">
            <div>
                <h1>Admin Page</h1>
            </div>
        </main>
    );
};

export default Admin;

上面的代码演示了如何在服务器组件上实现路由保护,确保只有经过身份验证的用户才能访问管理页面。如果未经身份验证的用户尝试访问此页面,将会被重定向到主页。

基于中间件

在 Next.js 中,利用中间件进行路由保护是一个不错的选择,它允许我们可以精确控制应用程序特定页面的访问。通过中间件函数,我们可以拦截请求,并在用户访问页面之前实施可访问性逻辑判断,从而确保只有符合特定条件的用户才能访问相关页面。

这是一种更强大更灵活的方法,适用于需要对路由访问进行精细控制的更复杂场景,尤其是在处理敏感数据、管理用户角色和权限时尤为常见。

基于中间件的路由保护既可以用于服务器组件也可以用于客户端组件。

在本示例中,我们将使用中间件保护 settings 页面。具体操作是创建一个middleware.ts文件。根据惯例,这个文件应该放在项目的根目录(与 app 或 pages 文件夹同级)或者 src 目录下(如果项目结构如此)。

然后,我们将通过添加以下代码来保护我们的 settings 页面:

//middleware.ts

import { isAuthenticated } from "@/Utils/Auth";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

const protectedRoutes = ["/settings"];

export default function middleware(req: NextRequest) {
    if (!isAuthenticated && protectedRoutes.includes(req.nextUrl.pathname)) {
        const absoluteURL = new URL("/", req.nextUrl.origin);
        return NextResponse.redirect(absoluteURL.toString());
    }
}

在示例代码中,我们定义了一个中间件函数,该函数当检测到用户未认证(!isAuthenticated),并且请求路径(req.nextUrl.pathname)属于受保护路由时,系统将使用NextResponse.redirect方法将用户重定向到首页。

在上面的代码中,isAuthenticated 是从 @/Utils/Auth 模块导入的。此函数负责检查用户的身份验证状态。如果用户经过身份验证,则返回 true,否则返回 false。

protectedRoutes是一个数组,其中包含了需要保护的路由的路径。在本示例中,它包含/settings路由。

结论

在 Web 应用程序开发中,确保路由的安全性是至关重要的,尤其是在涉及到敏感数据或需要对特定功能进行访问限制时。

通过本教程的学习,你应该已经掌握了在 Next.js 应用程序中实现路由保护的关键知识,这将有助你构建一个既安全又用户友好的 Web 应用程序。