跳到主要内容

如何在 Next.js 中实现身份验证

理解身份验证对于保护你的应用程序数据至关重要。本页将指导你了解使用哪些 React 和 Next.js 功能来实现身份验证。

在开始之前,将过程分解为三个概念会很有帮助:

  1. 身份验证:验证用户是否是他们声称的那个人。它要求用户用他们拥有的东西(如用户名和密码)来证明他们的身份。
  2. 会话管理:在请求之间跟踪用户的身份验证状态。
  3. 授权:决定用户可以访问哪些路由和数据。

此图显示了使用 React 和 Next.js 功能的身份验证流程:

显示使用 React 和 Next.js 功能的身份验证流程的图表显示使用 React 和 Next.js 功能的身份验证流程的图表

本页的示例介绍了用于教育目的的基本用户名和密码身份验证。虽然你可以实现自定义身份验证解决方案,但为了增加安全性和简化性,我们建议使用身份验证库。 这些库提供身份验证、会话管理和授权的内置解决方案,以及社交登录、多因素身份验证和基于角色的访问控制等附加功能。你可以在身份验证库部分找到列表。

身份验证

会话管理

会话管理确保用户认证状态在请求之间得到保留。它涉及创建、存储、刷新和删除会话或令牌。

有两种类型的会话:

  1. 无状态会话: 会话数据(或令牌)存储在浏览器的 cookie 中。cookie 随每个请求发送,允许服务器验证会话。这种方法更简单,但如果实现不当,则可能不那么安全。
  2. 数据库会话: 会话数据存储在数据库中,用户的浏览器只接收加密的会话 ID。这种方法更安全,但可能更复杂并使用更多服务器资源。

注意: 虽然你可以使用这两种方法,或两者都使用,但我们建议使用会话管理库,例如 iron-sessionJose

无状态会话

数据库会话

要创建和管理数据库会话,你需要遵循以下步骤:

  1. 在数据库中创建一个表来存储会话和数据(或检查你的认证库是否处理此问题)。
  2. 实现插入、更新和删除会话的功能
  3. 在用户浏览器中加密会话 ID 并确保数据库和 cookie 保持同步(这是可选的,但推荐用于乐观认证检查 中间件)。

授权

一旦用户认证并创建了会话,你就可以实现授权来控制用户可以在应用程序中访问和执行的操作。

有两种主要的授权检查类型:

  1. 乐观:检查 cookie 中存储的会话数据,以确定用户是否有权访问路由或执行操作。这些检查对于快速操作很有用,例如显示/隐藏 UI 元素或根据权限或角色重定向用户。
  2. 安全:检查数据库中存储的会话数据,以确定用户是否有权访问路由或执行操作。这些检查更安全,并用于需要访问敏感数据或执行操作的操作。

对于这两种情况,我们建议:

乐观检查与中间件(可选)

在某些情况下,你可能希望使用 中间件 并根据权限重定向用户:

  • 为了执行乐观检查。由于中间件在每个路由上运行,它是集中重定向逻辑和预过滤未授权用户的好方法。
  • 为了保护共享数据的路由(例如,内容在付费墙后面)。

然而,由于中间件在每个路由上运行,包括 预取 路由,重要的是只从 cookie 读取会话(乐观检查),并避免数据库检查以防止性能问题。

例如:

middleware.ts
import { NextRequest, NextResponse } from 'next/server'
import { decrypt } from '@/app/lib/session'
import { cookies } from 'next/headers'

// 1. 指定受保护和公共路由
const protectedRoutes = ['/dashboard']
const publicRoutes = ['/login', '/signup', '/']

export default async function middleware(req: NextRequest) {
// 2. 检查当前路由是否受保护或公共
const path = req.nextUrl.pathname
const isProtectedRoute = protectedRoutes.includes(path)
const isPublicRoute = publicRoutes.includes(path)

// 3. 从 cookie 解密会话
const cookie = (await cookies()).get('session')?.value
const session = await decrypt(cookie)

// 4. 如果用户未认证,重定向到 /login
if (isProtectedRoute && !session?.userId) {
return NextResponse.redirect(new URL('/login', req.nextUrl))
}

// 5. 如果用户已认证,重定向到 /dashboard
if (
isPublicRoute &&
session?.userId &&
!req.nextUrl.pathname.startsWith('/dashboard')
) {
return NextResponse.redirect(new URL('/dashboard', req.nextUrl))
}

return NextResponse.next()
}

// 中间件不应运行的路由
export const config = {
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
}

虽然中间件可以很有用,但它不应成为保护你数据的主要防线。大多数安全检查应该尽可能接近你的数据源,请参阅 数据访问层 了解更多信息。

Tips:

  • 在中间件中,你也可以使用 req.cookies.get('session').value 读取 cookie。
  • 中间件使用 Edge Runtime,请检查你的认证库和会话管理库是否兼容。
  • 你可以使用 Middleware 的 matcher 属性来指定 Middleware 应运行的路由。虽然,对于认证,建议 Middleware 运行在所有路由上。

资源

现在你已经了解了 Next.js 中的认证,这里有一些 Next.js 兼容的库和资源,可以帮助你实现安全的认证和会话管理:

认证库

会话管理库

进一步阅读

要继续了解认证和安全性,请查看以下资源: