技术篇:身份验证-NextAuth

·593 字·3 分钟
独立开发 Next.js
Next.js实战教程 - 系列文章
Part 8: 当前文章

学习目标:为应用接入用户登录功能

NextAuth.js 简介 #

随着现代Web应用的复杂性不断增加,用户认证和授权变得至关重要。NextAuth.js 是一个开源的用户认证库,支持多种身份验证方式,包括 OAuth、邮件登录、凭证登录等。它专为 Next.js 设计,但也可以集成到其他 React 应用中。通过其丰富的配置选项和内置的安全特性,开发者可以快速搭建一个功能强大的认证系统。

核心概念与功能 #

Provider(认证提供者) #

Provider 是 NextAuth.js 用于实现身份验证的核心,NextAuth.js 支持多种认证提供者,如 OAuth Providers:Google, Facebook, GitHub, Twitter 等第三方服务,Email Provider:基于邮箱的登录方式,以及 Credentials Provider:允许开发者自定义基于用户名和密码的登录系统。

Session 管理 #

NextAuth.js 支持两种 Session 管理策略,通过 JWT(JSON Web Token)或基于数据库的会话管理来保持用户的登录状态。JWT Session 为默认策略,本文的演示也给予默认策略。

JWT(JSON Web Token) #

NextAuth.js 支持无状态身份验证,使用 JWT 存储会话信息。

数据库集成 #

为了保存用户信息和会话数据,NextAuth.js 可以与各种数据库(如 MongoDB、PostgreSQL、MySQL)集成。

Cookies #

成功登录后,NextAuth 会在你的浏览器中设置一组 Cookies。

Callbacks(回调函数) #

NextAuth.js 提供了多种回调函数,允许开发者在身份验证的不同阶段执行自定义逻辑。这些回调函数包括:

  • jwt:在生成或更新 JWT 时调用。
  • session:在生成或更新会话时调用。
  • signIn:在用户尝试登录时调用,可以控制是否允许登录。
  • redirect:控制登录或注销后的重定向行为。

内置安全机制 #

提供了多种内置的安全机制,例如 CSRF 保护、JWT 加密,确保身份验证流程的安全性。

实战示例 #

项目初始化 #

npx create-next-app@latest

 What is your project named?  example-auth
 Would you like to use TypeScript?  No / Yes
 Would you like to use ESLint?  No / Yes
 Would you like to use Tailwind CSS?  No / Yes
 Would you like to use `src/` directory?  No / Yes
 Would you like to use App Router? (recommended)  No / Yes
 Would you like to customize the default import alias (@/*)?  No / Yes

image.png

安装 Next-Auth #

首先需要安装 NextAuth.js 和相关依赖:

yarn add next-auth

创建 GitHub OAuth 应用 #

在集成 GitHub 登录之前,你需要创建一个 GitHub OAuth 应用来获取 Client IDClient Secret

  1. 登录 GitHub Developer Settings
  2. 在 “OAuth Apps” 下,点击 “New OAuth App”。
  3. 填写必要的信息:
    • Application name:你的应用名称。
    • Homepage URL:如 http://localhost:3000(本地开发时使用)。
    • Authorization callback URLhttp://localhost:3000/api/auth/callback/github(NextAuth.js 默认的 GitHub 回调 URL)。
  4. 创建应用后,你将获得 Client IDClient Secret

基本配置 #

app/api 目录下创建 auth/[...nextauth]/route.js 文件:

// app/api/auth/[...nextauth]/route.js
import NextAuth from "next-auth";
import GitHubProvider from "next-auth/providers/github";

const handler = NextAuth({
  providers: [
    GitHubProvider({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
  ],
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.sub; // 将用户ID附加到会话中
      return session;
    },
  },
});

export { handler as GET, handler as POST };

这个 API 路由将处理 GitHub 的 OAuth 身份验证过程。NextAuth.js 默认会处理 GET 和 POST 请求,因此需要导出 GETPOST

设置环境变量 #

将 GitHub 应用的 Client IDClient Secret 配置到 .env 文件中:

GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
NEXTAUTH_URL=http://localhost:3000  # 本地开发时使用

确保用你从 GitHub 获取的 Client IDClient Secret 替换相应的占位符。

生成和配置 NEXTAUTH_SECRET #

你可以通过运行以下命令生成一个随机的 32 字节的密钥:

openssl rand -base64 32

生成的密钥可能像这样:

base64encodedsecretstring==

NEXTAUTH_SECRET 配置到 .env 文件中:

NEXTAUTH_SECRET=base64encodedsecretstring==
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
NEXTAUTH_URL=http://localhost:3000

NEXTAUTH_SECRET,用于加密会话令牌(JWT)等。

在客户端使用 useSession 管理会话 #

在 Next.js 13 中,使用 useSession 在页面或组件中显示用户的登录状态,并提供登录/注销按钮。

app/page.js 文件中,添加以下代码:

// app/page.js;
"use client";
import { useSession, signIn, signOut } from "next-auth/react";

export default function HomePage() {
  const { data: session, status } = useSession();

  if (status === "loading") {
    return <p>Loading...</p>;
  }

  return (
    <div className="p-10">
      {!session ? (
        <>
          <p>Not signed in</p>
          <button onClick={() => signIn("github")}>Sign in with GitHub</button>
        </>
      ) : (
        <>
          <p>Signed in as {session.user.email}</p>
          <img
            className="w-[100px] h-[100px]"
            src={session.user.image}
            alt={session.user.name}
            style={{ borderRadius: "50%" }}
          />
          <button onClick={() => signOut()}>Sign out</button>
        </>
      )}
    </div>
  );
}
  • useSession 钩子获取当前用户的会话信息。
  • 如果用户未登录,会显示一个 GitHub 登录按钮。
  • 登录后,显示用户的邮箱和头像,同时提供注销按钮。

配置 next-auth 提供的 Provider #

由于 App Router 是完全异步的,SessionProvider 必须包裹在根布局中。你需要在 app/layout.js 中配置 SessionProvider 以提供会话上下文。

  1. app 目录创建 Provider.js
"use client";
import { SessionProvider } from "next-auth/react";

export function Provider({ children }) {
  return <SessionProvider>{children}</SessionProvider>;
}
  1. app/layout.js 文件中添加 Providers
// app/layout.js
import "./globals.css";
import { Provider } from "./Provider";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <Provider>{children}</Provider>
      </body>
    </html>
  );
}

SessionProvider 确保会话信息可以在应用中的所有组件中访问。

运行应用 #

最后,在终端中运行 Next.js 应用:

yarn dev

访问 http://localhost:3000,你会看到登录界面。点击 “Sign in with GitHub”,并使用 GitHub 账号进行登录。

image <em>1</em>.png

image <em>2</em>.png

image <em>3</em>.png

回调与安全配置(可选) #

你可以在 callbacks 选项中进一步自定义登录过程,比如获取用户的更多 GitHub 信息:

callbacks: {
  async signIn({ user, account, profile }) {
    return profile.email.endsWith("@example.com") // 仅允许特定域名的 GitHub 用户登录
  },
  async jwt({ token, account }) {
    if (account) {
      token.accessToken = account.access_token
    }
    return token
  },
  async session({ session, token }) {
    session.accessToken = token.accessToken
    return session
  },
}

总结 #

NextAuth.js 是一个功能强大且灵活的认证解决方案,适合各种规模的应用。从简单的 OAuth 集成到复杂的自定义认证逻辑,它几乎可以满足所有的需求。通过本文我们正式接入登录功能。

参考 #

https://github.com/nextauthjs/next-auth

https://next-auth.js.org/

https://authjs.dev/concepts/session-strategies

https://next-auth.js.org/configuration/options#nextauth_secret

https://stackoverflow.com/questions/74992326/does-use-client-in-next-js-13-root-layout-make-whole-routes-client-component

https://dev.to/peterlidee/full-guide-for-authentication-with-next-14-nextauth-4-strapi-v4-using-google-and-credentials-provider-7jh

Next.js实战教程 - 系列文章
Part 8: 当前文章