Next.js 身份验证与授权:使用 NextAuth.js 保护你的应用

作者:码力无边


随着我们的应用变得越来越复杂,一个不可避免的需求浮出水面:用户系统。我们需要允许用户注册、登录,并根据他们的身份来决定他们可以访问哪些页面、看到哪些内容。这个过程就是身份验证 (Authentication)授权 (Authorization)

  • 身份验证 (AuthN):确认“你是谁?”。这通常通过用户名密码、社交登录(Google, GitHub)或魔法链接(Magic Links)来完成。
  • 授权 (AuthZ):确认“你能做什么?”。一旦用户的身份被确认,我们就需要根据其角色或权限来控制其访问范围。

在 Next.js 中手动实现一个安全、健壮的认证系统是一项极其复杂且容易出错的任务。你需要处理密码哈希、会话管理(Cookies, JWT)、CSRF 保护、多种登录方式(Providers)的集成等等。

幸运的是,社区为我们提供了一个近乎完美的解决方案:NextAuth.js。这是一个专为 Next.js 设计的、功能齐全、配置简单的开源认证库。它将所有认证的复杂性都封装了起来,让你只需几行代码就能为应用添加强大的认证功能。

为什么选择 NextAuth.js?

  1. 开箱即用,功能全面:支持数十种常见的 OAuth 提供商(Google, GitHub, Facebook 等)、邮箱/密码登录、魔法链接、以及自定义凭证验证。
  2. 安全可靠:内置了 CSRF 保护、安全的 Cookie 策略(httpOnly, sameSite),并遵循安全最佳实践。
  3. 无缝集成:与 Next.js 的服务端环境(SSR, API 路由, Server Components)完美配合,提供了便捷的 Hooks 和辅助函数来在客户端和服务端访问会话信息。
  4. 无数据库或有数据库均可:它可以将会话信息存储在无状态的 JWT (JSON Web Tokens) 中,也可以通过适配器 (Adapters) 将用户和会话数据持久化到你选择的数据库中(如 Prisma, TypeORM)。
  5. 高度可定制:你可以通过回调 (Callbacks) 函数深度定制会话、JWT 的内容以及登录流程。

实战:为我们的 App Router 应用添加 GitHub 登录

我们将通过一个具体的例子,为我们的应用添加“使用 GitHub 登录”的功能,并保护一个 /dashboard 页面。

步骤一:安装依赖

npm install next-auth

步骤二:在 GitHub 上创建 OAuth App

  1. 登录你的 GitHub 账户,进入 Settings > Developer settings > OAuth Apps > New OAuth App
  2. Application name: 任意填写,如 My Next.js Blog
  3. Homepage URL: 你的应用的 URL,开发环境下填写 http://localhost:3000
  4. Authorization callback URL: 这是一个关键字段。对于 NextAuth.js,它遵循一个固定格式。开发环境下填写 http://localhost:3000/api/auth/callback/github
  5. 创建应用后,你会得到一个 Client ID。然后,生成一个新的 Client Secret。将这两个值安全地保存到你的 .env.local 文件中。

.env.local

GITHUB_ID=your_client_id
GITHUB_SECRET=your_client_secret

# NextAuth.js 需要一个密钥来加密 JWT
# 你可以用 `openssl rand -base64 32` 命令生成一个
NEXTAUTH_SECRET=a_super_secret_string_for_production
NEXTAUTH_URL=http://localhost:3000

NEXTAUTH_URL 环境变量在开发和生产环境中都很重要,务必设置。

步骤三:创建 NextAuth.js 的 API 路由

NextAuth.js 的核心是一个动态 API 路由,它会处理所有与认证相关的请求(如登录、登出、回调等)。

app/api/auth/[...nextauth]/route.ts 创建这个文件。注意 [...nextauth] 这个文件名是固定的。

app/api/auth/[...nextauth]/route.ts

import NextAuth from 'next-auth';
import GitHubProvider from 'next-auth/providers/github';
import { AuthOptions } from 'next-auth';

export const authOptions: AuthOptions = {
  // 1. 配置认证提供商
  providers: [
    GitHubProvider({
      clientId: process.env.GITHUB_ID as string,
      clientSecret: process.env.GITHUB_SECRET as string,
    }),
    // ... 在这里可以添加其他提供商,如 GoogleProvider
  ],
  // 可以在这里添加 callbacks, pages, session 等高级配置
};

const handler = NextAuth(authOptions);

// Next.js App Router 要求我们导出 GET 和 POST
export { handler as GET, handler as POST };

现在,你的认证后端已经配置完毕!访问 http://localhost:3000/api/auth/signin,你应该能看到一个由 NextAuth.js 自动生成的登录页面,上面有一个“使用 GitHub 登录”的按钮。

步骤四:在应用中管理会话 (SessionProvider)

为了在客户端组件中方便地访问用户会话信息(例如,用户是否登录,用户的名字和头像),我们需要在应用的根布局中使用 SessionProvider 来包裹我们的应用。

components/AuthProvider.tsx (创建一个新的客户端组件来包裹 SessionProvider)

"use client";

import { SessionProvider } from "next-auth/react";

type Props = {
  children?: React.ReactNode;
};

export default function AuthProvider({ children }: Props) {
  return <SessionProvider>{children}</SessionProvider>;
}

app/layout.tsx

import AuthProvider from '@/components/AuthProvider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AuthProvider>
          {/* ... 你的导航栏等其他布局组件 ... */}
          {children}
        </AuthProvider>
      </body>
    </html>
  );
}

步骤五:创建登录/登出按钮和受保护的页面

现在我们可以在客户端组件中使用 useSession, signIn, signOut 等方法了。

components/LoginButton.tsx

"use president";

import { useSession, signIn, signOut } from "next-auth/react";

export default function LoginButton() {
  const { data: session } = useSession();

  if (session) {
    // 如果用户已登录
    return (
      <>
        欢迎回来, {session.user?.name} <br />
        <img src={session.user?.image ?? ''} alt="avatar" style={{ width: 50, borderRadius: '50%' }} />
        <button onClick={() => signOut()}>退出登录</button>
      </>
    );
  }

  // 如果用户未登录
  return (
    <>
      您尚未登录 <br />
      <button onClick={() => signIn("github")}>使用 GitHub 登录</button>
    </>
  );
}

你可以在你的导航栏或首页中使用这个 <LoginButton /> 组件。

步骤六:保护服务端页面/路由

NextAuth.js 提供了在服务端获取会话信息的方法,这对于在 Server Components 中进行授权检查或在中间件中保护路由至关重要。

在 Server Component 中获取会Session

让我们创建一个受保护的仪表盘页面。

app/dashboard/page.tsx

import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { redirect } from 'next/navigation';

export default async function DashboardPage() {
  const session = await getServerSession(authOptions);

  if (!session) {
    // 如果没有 session,重定向到登录页
    redirect('/api/auth/signin?callbackUrl=/dashboard');
  }

  return (
    <div>
      <h1>仪表盘</h1>
      <p>这是一个受保护的页面。只有登录用户才能看到。</p>
      <p>你的用户信息: {JSON.stringify(session.user)}</p>
    </div>
  );
}

使用中间件进行全局保护 (更推荐)

对于需要保护的一整块区域(如所有 /admin/* 路径),使用中间件是更高效、更优雅的方式。

middleware.ts

export { default } from "next-auth/middleware";

// 使用 matcher 来指定需要保护的路径
export const config = { matcher: ["/dashboard/:path*"] };

仅仅这两行代码!next-auth/middleware 会自动处理所有逻辑:检查 session cookie,如果没有或无效,则自动将用户重定向到 authOptions 中配置的登录页面。这是保护路由的最推荐方式。

总结

NextAuth.js 是为 Next.js 应用添加身份验证功能的黄金标准。它将认证流程中的所有复杂性和安全隐患都抽象掉了,让开发者可以专注于业务逻辑。

本次实战核心回顾:

  1. 配置驱动:通过在 [...nextauth] 路由中配置 authOptions 来定义你的认证策略。
  2. 客户端访问:在根布局中添加 SessionProvider,然后在客户端组件中使用 useSession Hook 来获取会话状态。
  3. 服务端访问:在 Server Components 中使用 getServerSession,或在中间件中使用 next-auth/middleware 来实现路由保护和数据授权。

通过集成 NextAuth.js,你的应用现在拥有了一个完整的、安全的用户系统。这是构建任何真实世界应用(如 SaaS、电商、社交平台)都不可或缺的一步。

在下一篇文章中,我们将探讨另一个在大型应用中至关重要的话题:状态管理。我们将讨论在 Server Components 和 Client Components 并存的新范式下,如何选择和使用合适的状态管理方案。敬请期待!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐