验证
¥Authentication
了解身份验证对于保护应用的数据至关重要。本页面将指导你了解用于实现身份验证的 React 和 Next.js 功能。
¥Understanding authentication is crucial for protecting your application's data. This page will guide you through what React and Next.js features to use to implement auth.
在开始之前,将流程分解为三个概念会有所帮助:
¥Before starting, it helps to break down the process into three concepts:
验证:验证用户是否是他们所说的人。它要求用户使用他们拥有的东西来证明他们的身份,例如用户名和密码。
¥Authentication: Verifies if the user is who they say they are. It requires the user to prove their identity with something they have, such as a username and password.
会话管理:跨请求跟踪用户的身份验证状态。
¥Session Management: Tracks the user's auth state across requests.
授权:决定用户可以访问哪些路由和数据。
¥Authorization: Decides what routes and data the user can access.
下图显示了使用 React 和 Next.js 功能的身份验证流程:
¥This diagram shows the authentication flow using React and Next.js features:

本页面上的示例演示了用于教育目的的基本用户名和密码身份验证。虽然你可以实现自定义身份验证解决方案,但为了提高安全性和简单性,我们建议使用身份验证库。它们提供了用于身份验证、会话管理和授权的内置解决方案,以及社交登录、多因素身份验证和基于角色的访问控制等附加功能。你可以在 授权库 部分找到列表。
¥The examples on this page walk through basic username and password auth for educational purposes. While you can implement a custom auth solution, for increased security and simplicity, we recommend using an authentication library. These offer built-in solutions for authentication, session management, and authorization, as well as additional features such as social logins, multi-factor authentication, and role-based access control. You can find a list in the Auth Libraries section.
验证
¥Authentication
以下是实现注册和/或登录表单的步骤:
¥Here are the steps to implement a sign-up and/or login form:
用户通过表单提交其凭据。
¥The user submits their credentials through a form.
该表单发送一个由 API 路由处理的请求。
¥The form sends a request that is handled by an API route.
验证成功后,该过程完成,表示用户身份验证成功。
¥Upon successful verification, the process is completed, indicating the user's successful authentication.
如果验证不成功,则会显示错误消息。
¥If verification is unsuccessful, an error message is shown.
考虑一个用户可以输入其凭据的登录表单:
¥Consider a login form where users can input their credentials:
import { FormEvent } from 'react'
import { useRouter } from 'next/router'
export default function LoginPage() {
const router = useRouter()
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
const email = formData.get('email')
const password = formData.get('password')
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (response.ok) {
router.push('/profile')
} else {
// Handle errors
}
}
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
)
}
上面的表单有两个输入字段,用于获取用户的电子邮件地址和密码。提交时,它会触发一个函数,向 API 路由(/api/auth/login
)发送 POST 请求。
¥The form above has two input fields for capturing the user's email and password. On submission, it triggers a function that sends a POST request to an API route (/api/auth/login
).
然后,你可以在 API 路由中调用身份验证提供程序的 API 来处理身份验证:
¥You can then call your Authentication Provider's API in the API route to handle authentication:
import type { NextApiRequest, NextApiResponse } from 'next'
import { signIn } from '@/auth'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email, password } = req.body
await signIn('credentials', { email, password })
res.status(200).json({ success: true })
} catch (error) {
if (error.type === 'CredentialsSignin') {
res.status(401).json({ error: 'Invalid credentials.' })
} else {
res.status(500).json({ error: 'Something went wrong.' })
}
}
}
会话管理
¥Session Management
会话管理可确保跨请求保留用户的身份验证状态。它涉及创建、存储、刷新和删除会话或令牌。
¥Session management ensures that the user's authenticated state is preserved across requests. It involves creating, storing, refreshing, and deleting sessions or tokens.
有两种类型的会话:
¥There are two types of sessions:
无国籍:会话数据(或令牌)存储在浏览器的 cookie 中。cookie 随每个请求一起发送,从而允许在服务器上验证会话。此方法更简单,但如果实现不正确,安全性可能会降低。
¥Stateless: Session data (or a token) is stored in the browser's cookies. The cookie is sent with each request, allowing the session to be verified on the server. This method is simpler, but can be less secure if not implemented correctly.
数据库:会话数据存储在数据库中,用户的浏览器仅接收加密的会话 ID。此方法更安全,但可能很复杂并且使用更多服务器资源。
¥Database: Session data is stored in a database, with the user's browser only receiving the encrypted session ID. This method is more secure, but can be complex and use more server resources.
需要了解:虽然你可以使用任一方法或两者,但我们建议使用会话管理库,例如 iron-session 或 何塞。
¥Good to know: While you can use either method, or both, we recommend using a session management library such as iron-session or Jose.
无状态会话
¥Stateless Sessions
设置和删除 Cookie
¥Setting and deleting cookies
你可以使用 API 路由 将会话设置为服务器上的 Cookie:
¥You can use API Routes to set the session as a cookie on the server:
import { serialize } from 'cookie'
import type { NextApiRequest, NextApiResponse } from 'next'
import { encrypt } from '@/app/lib/session'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const sessionData = req.body
const encryptedSessionData = encrypt(sessionData)
const cookie = serialize('session', encryptedSessionData, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7, // One week
path: '/',
})
res.setHeader('Set-Cookie', cookie)
res.status(200).json({ message: 'Successfully set cookie!' })
}
数据库会话
¥Database Sessions
要创建和管理数据库会话,你需要执行以下步骤:
¥To create and manage database sessions, you'll need to follow these steps:
在数据库中创建一个表来存储会话和数据(或检查你的身份验证库是否处理此问题)。
¥Create a table in your database to store session and data (or check if your Auth Library handles this).
实现插入、更新和删除会话的功能
¥Implement functionality to insert, update, and delete sessions
在将会话 ID 存储到用户浏览器之前对其进行加密,并确保数据库和 cookie 保持同步(这是可选的,但建议用于 中间件 中的乐观身份验证检查)。
¥Encrypt the session ID before storing it in the user's browser, and ensure the database and cookie stay in sync (this is optional, but recommended for optimistic auth checks in Middleware).
在服务器上创建会话:
¥Creating a Session on the Server:
import db from '../../lib/db'
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const user = req.body
const sessionId = generateSessionId()
await db.insertSession({
sessionId,
userId: user.id,
createdAt: new Date(),
})
res.status(200).json({ sessionId })
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
授权
¥Authorization
用户通过身份验证并创建会话后,你可以实现授权来控制用户可以在应用中访问和执行的操作。
¥Once a user is authenticated and a session is created, you can implement authorization to control what the user can access and do within your application.
授权检查主要有两种类型:
¥There are two main types of authorization checks:
乐观:检查用户是否有权使用存储在 cookie 中的会话数据访问路由或执行操作。这些检查对于快速操作非常有用,例如显示/隐藏 UI 元素或根据权限或角色重定向用户。
¥Optimistic: Checks if the user is authorized to access a route or perform an action using the session data stored in the cookie. These checks are useful for quick operations, such as showing/hiding UI elements or redirecting users based on permissions or roles.
安全:检查用户是否有权访问路由或使用数据库中存储的会话数据执行操作。这些检查更加安全,用于需要访问敏感数据或操作的操作。
¥Secure: Checks if the user is authorized to access a route or perform an action using the session data stored in the database. These checks are more secure and are used for operations that require access to sensitive data or actions.
对于这两种情况,我们建议:
¥For both cases, we recommend:
创建 数据访问层 来集中你的授权逻辑
¥Creating a Data Access Layer to centralize your authorization logic
使用 数据传输对象 (DTO) 仅返回必要的数据
¥Using Data Transfer Objects (DTO) to only return the necessary data
可以选择使用 中间件 执行乐观检查。
¥Optionally use Middleware to perform optimistic checks.
使用中间件进行乐观检查(可选)
¥Optimistic checks with Middleware (Optional)
在某些情况下,你可能希望使用 中间件 并根据权限重定向用户:
¥There are some cases where you may want to use Middleware and redirect users based on permissions:
要执行乐观检查。由于中间件在每条路由上运行,因此这是集中重定向逻辑和预过滤未经授权的用户的好方法。
¥To perform optimistic checks. Since Middleware runs on every route, it's a good way to centralize redirect logic and pre-filter unauthorized users.
保护在用户之间共享数据的静态路由(例如付费专区后面的内容)。
¥To protect static routes that share data between users (e.g. content behind a paywall).
然而,由于中间件在每个路由上运行,包括 prefetched 路由,因此仅从 cookie 读取会话(乐观检查)并避免数据库检查以防止性能问题非常重要。
¥However, since Middleware runs on every route, including prefetched routes, it's important to only read the session from the cookie (optimistic checks), and avoid database checks to prevent performance issues.
例如:
¥For example:
import { NextRequest, NextResponse } from 'next/server'
import { decrypt } from '@/app/lib/session'
import { cookies } from 'next/headers'
// 1. Specify protected and public routes
const protectedRoutes = ['/dashboard']
const publicRoutes = ['/login', '/signup', '/']
export default async function middleware(req: NextRequest) {
// 2. Check if the current route is protected or public
const path = req.nextUrl.pathname
const isProtectedRoute = protectedRoutes.includes(path)
const isPublicRoute = publicRoutes.includes(path)
// 3. Decrypt the session from the cookie
const cookie = (await cookies()).get('session')?.value
const session = await decrypt(cookie)
// 4. Redirect to /login if the user is not authenticated
if (isProtectedRoute && !session?.userId) {
return NextResponse.redirect(new URL('/login', req.nextUrl))
}
// 5. Redirect to /dashboard if the user is authenticated
if (
isPublicRoute &&
session?.userId &&
!req.nextUrl.pathname.startsWith('/dashboard')
) {
return NextResponse.redirect(new URL('/dashboard', req.nextUrl))
}
return NextResponse.next()
}
// Routes Middleware should not run on
export const config = {
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
}
虽然中间件对于初始检查很有用,但它不应该是保护数据的唯一防线。大多数安全检查应尽可能靠近你的数据源执行,请参阅 数据访问层 了解更多信息。
¥While Middleware can be useful for initial checks, it should not be your only line of defense in protecting your data. The majority of security checks should be performed as close as possible to your data source, see Data Access Layer for more information.
提示:
¥Tips:
在中间件中,你还可以使用
req.cookies.get('session').value
读取 cookie。¥In Middleware, you can also read cookies using
req.cookies.get('session').value
.中间件使用 Edge 运行时,检查你的 Auth 库和会话管理库是否兼容。
¥Middleware uses the Edge Runtime, check if your Auth library and session management library are compatible.
你可以使用中间件中的
matcher
属性来指定中间件应在哪些路由上运行。不过,对于身份验证,建议中间件在所有路由上运行。¥You can use the
matcher
property in the Middleware to specify which routes Middleware should run on. Although, for auth, it's recommended Middleware runs on all routes.
创建数据访问层 (DAL)
¥Creating a Data Access Layer (DAL)
保护 API 路由
¥Protecting API Routes
Next.js 中的 API 路由对于处理服务器端逻辑和数据管理至关重要。保护这些路由的安全至关重要,以确保只有授权用户才能访问特定功能。这通常涉及验证用户的身份验证状态及其基于角色的权限。
¥API Routes in Next.js are essential for handling server-side logic and data management. It's crucial to secure these routes to ensure that only authorized users can access specific functionalities. This typically involves verifying the user's authentication status and their role-based permissions.
以下是保护 API 路由的示例:
¥Here's an example of securing an API Route:
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getSession(req)
// Check if the user is authenticated
if (!session) {
res.status(401).json({
error: 'User is not authenticated',
})
return
}
// Check if the user has the 'admin' role
if (session.user.role !== 'admin') {
res.status(401).json({
error: 'Unauthorized access: User does not have admin privileges.',
})
return
}
// Proceed with the route for authorized users
// ... implementation of the API Route
}
此示例演示了一个 API 路由,其中包含用于身份验证和授权的两层安全检查。它首先检查活动会话,然后验证登录用户是否为 'admin'。此方法可确保安全访问,仅限于经过身份验证和授权的用户,从而为请求处理保持强大的安全性。
¥This example demonstrates an API Route with a two-tier security check for authentication and authorization. It first checks for an active session, and then verifies if the logged-in user is an 'admin'. This approach ensures secure access, limited to authenticated and authorized users, maintaining robust security for request processing.
资源
¥Resources
现在你已经了解了 Next.js 中的身份验证,以下是与 Next.js 兼容的库和资源,可帮助你实现安全身份验证和会话管理:
¥Now that you've learned about authentication in Next.js, here are Next.js-compatible libraries and resources to help you implement secure authentication and session management:
授权库
¥Auth Libraries
会话管理库
¥Session Management Libraries
进一步阅读
¥Further Reading
要继续了解身份验证和安全性,请查看以下资源:
¥To continue learning about authentication and security, check out the following resources: