添加身份验证
在上一章节中,您通过添加表单验证和改进可访问性完成了发票路由的构建。本章将学习如何为仪表板添加身份验证功能。
什么是身份验证?
身份验证是现代网络应用的核心组成部分,用于验证用户身份的真实性。
安全的网站通常会采用多种方式验证用户身份。例如,在输入用户名和密码后,网站可能会向您的设备发送验证码,或使用 Google Authenticator 等外部应用进行二次验证。这种双重验证 (2FA) 机制能有效提升安全性——即使密码泄露,攻击者也无法通过没有动态令牌的设备登录您的账户。
身份验证与授权
在 Web 开发中,身份验证和授权具有不同功能:
- 身份验证 确认用户身份的真实性,通常通过用户名/密码等凭证进行验证
- 授权 是在身份验证之后的步骤,决定用户有权访问应用的哪些部分
简而言之,身份验证解决"你是谁"的问题,而授权解决"你能做什么"的问题。
创建登录路由
首先在应用中创建 /login
路由,并粘贴以下代码:
注意此页面导入的 <LoginForm />
组件将在本章后续更新。该组件使用 React <Suspense>
包裹,因为它需要访问请求中的信息(URL 搜索参数)。
NextAuth.js
我们将使用 NextAuth.js 为应用添加身份验证功能。NextAuth.js 抽象了会话管理、登录/登出等复杂逻辑。虽然您可以手动实现这些功能,但过程既耗时又容易出错。NextAuth.js 为 Next.js 应用提供了一站式身份验证解决方案。
设置 NextAuth.js
通过以下命令安装 NextAuth.js:
这里安装的是 NextAuth.js 的 beta
版本,兼容 Next.js 14+。
接下来生成应用密钥用于加密 cookies 保障会话安全:
然后在 .env
文件中添加密钥:
生产环境需在 Vercel 项目中同步更新环境变量,参考 环境变量配置指南。
添加页面选项
在项目根目录创建 auth.config.ts
文件,导出包含 NextAuth.js 配置的 authConfig
对象:
通过 pages
选项可自定义登录/登出页面的路由。虽然非必需,但设置 signIn: '/login'
后用户将被重定向至自定义登录页而非默认页面。
使用 Next.js 中间件保护路由
添加路由保护逻辑防止未登录用户访问仪表板:
authorized
回调函数通过 Next.js 中间件 验证请求权限,在请求完成前执行并接收包含 auth
(用户会话) 和 request
(请求对象) 的参数。
providers
数组用于配置登录方式,目前为空数组,后续将在 添加凭证提供者 部分详细介绍。
在项目根目录创建 middleware.ts
文件:
这里使用 authConfig
初始化 NextAuth.js 并导出 auth
属性,同时通过 matcher
指定中间件运行的路径。
使用中间件的优势在于:受保护路由会在渲染前完成身份验证,既提升安全性又优化性能。
密码哈希
存储密码前进行 哈希处理 是安全最佳实践。哈希将密码转换为固定长度的随机字符串,即使数据泄露也能提供安全保障。
数据库初始化时已使用 bcrypt
包对密码进行哈希存储。本章后续将再次使用它验证用户输入密码与数据库记录的匹配性。由于 bcrypt
依赖 Node.js API(Next.js 中间件不可用),需要创建单独文件:
添加凭证提供者
在 NextAuth.js 中添加 providers
配置。虽然支持 Google/GitHub 等登录方式,本章重点介绍 凭证提供者:
须知:
其他登录方式如 OAuth 或 邮件登录,完整列表参见 NextAuth.js 文档。
实现登录功能
通过 authorize
函数处理认证逻辑,类似服务端操作,使用 zod
验证邮箱密码后检查用户是否存在:
验证凭证后创建 getUser
函数查询数据库:
最后通过 bcrypt.compare
验证密码匹配性:
密码匹配时返回用户对象,否则返回 null
阻止登录。
更新登录表单
现在您需要将认证逻辑与登录表单连接。在 actions.ts
文件中,创建一个名为 authenticate
的新操作。该操作应从 auth.ts
导入 signIn
函数:
如果出现 'CredentialsSignin'
错误,您需要显示相应的错误消息。您可以在 NextAuth.js 文档 中了解更多关于错误的信息。
最后,在您的 login-form.tsx
组件中,可以使用 React 的 useActionState
来调用服务器操作、处理表单错误并显示表单的待处理状态:
添加退出功能
要为 <SideNav />
添加退出功能,请在 <form>
元素中调用 auth.ts
中的 signOut
函数:
尝试使用
现在可以尝试一下。您应该能够使用以下凭证登录和退出应用程序:
- 邮箱:
[email protected]
- 密码:
123456