type
Post
status
Published
date
Dec 24, 2025
slug
nextjs-009
summary
09 - User Authentication
tags
Next.js
category
编程学习
icon
password
Lucia v3已被弃用。Lucia现在是一个用于实现会话等功能的学习资源。
001 Module Introduction
身份验证:详细、逐步地讲解身份验证相关内容,包括如何创建用户(实现用户注册)、让现有用户登录并验证其凭证,以及保护特定路由(仅让已登录并验证的用户访问部分路由)。
002 Project Setup
一、项目初始化核心操作
- 项目获取与依赖安装
- 先按常规方式下载已准备好的新品牌项目。
- 下载后执行命令
npm install,安装项目所需的所有依赖项。
- 开发服务器启动
- 依赖安装完成后,运行
npm run dev启动开发服务器,启动后可进行后续页面查看与开发。
二、关键页面与访问路径
页面功能 | 访问路径 | 页面状态与后续规划 |
登录屏幕 | localhost 3000 | 当前为项目初始展示页面,后续将基于此实现新用户创建与登录功能 |
培训假页面 | localhost 4000 training | 含基础培训内容,当前所有访问者均可查看,后续需设置权限,仅允许已验证用户访问 |
三、项目核心文件与组件解析
- UI 组件:auth form 组件
- 位置:位于项目的“组件文件夹”中。
- 构成:包含表单、图像、输入框以及待开发的“使用现有帐户登录”链接。
- 当前状态:表单提交无响应,“使用现有帐户登录”链接暂未实现功能,后续需完善交互逻辑。
- 数据库相关文件
- lip 文件夹与 db.js:负责项目数据库初始化,创建用户表、身份验证会话表、训练项表,同时在
trainings表中存储假训练数据,暂不创建用户与会话,留待后续开发。 - training.js 文件:功能单一,用于检索
trainings表中的假训练数据,支撑培训页面内容展示。
- 路由与布局配置:app 文件夹
- 路由设置:根路由渲染 auth form 组件(对应登录页面),
training.js对应培训页面路由。 - 布局作用:统一管理项目页面布局,确保页面风格与结构一致性。
四、项目阶段性开发计划
- 第一步核心目标:完善登录表单功能,实现通过有效数据创建新用户的功能。
- 后续规划:完成新用户创建后,逐步开发用户登录、培训页面权限控制等功能,逐步迭代为完整应用。
003 User Signup Extracting & Validating User Input - 注册功能
核心知识点框架
知识点分类 | 核心内容 |
服务器端操作 | use-server 指令、服务器端函数定义、表单数据接收/验证 |
客户端交互 | use-client 指令、useFormState 钩子、表单联动、错误展示 |
前后端联动 | 表单 action 关联服务器函数、状态传递、错误对象返回/渲染 |
一、服务器端操作(核心)
1. 基础配置:服务器端文件声明
- 核心规则:Next.js 中服务器端操作文件需通过
use-server指令声明,该文件内的函数仅在服务器端执行。
- 文件结构:项目根目录新建
actions文件夹,存放所有服务器端操作函数(如auth.js)。
2. 注册函数实现:数据提取与验证
- 参数规则:使用
useFormState时,服务器函数第一个参数为「前一次表单状态」,第二个参数为「表单提交数据」。
- 验证逻辑:邮箱检查
@符号、密码检查长度(≥8),错误信息存入对象返回。
二、客户端表单组件(核心)
1. 客户端组件声明与钩子导入
- 核心钩子:
useFormState(React DOM 内置)用于管理表单状态,仅在客户端组件中生效,需声明use-client。
- 钩子参数:第一个参数为「服务器端注册函数」,第二个参数为「初始状态」。
三、关键注意事项
- 指令位置:
use-server/use-client必须置顶,且一个文件只能声明一种指令。
- 字段匹配:表单
input的name属性必须与服务器端formData.get()的参数一致。
- 状态传递:
useFormState会自动将服务器函数的返回值作为「当前表单状态」,前端可直接读取。
- 错误处理:服务器端返回的错误对象结构需与前端渲染逻辑匹配(如示例中
errors.email/errors.password)。
四、核心流程总结
- 客户端表单提交 → 触发
useFormState包裹的服务器函数。
- 服务器端提取表单数据 → 执行验证逻辑 → 返回错误/成功状态。
- 客户端接收服务器返回的状态 → 渲染错误提示/成功信息。
- 验证通过后,扩展服务器函数实现「用户创建、数据入库」等核心业务逻辑。
004 Storing Users in a Database - The Wrong Way - 数据库存储用户信息(密码)
一、核心问题:明文存储密码的风险
1. 核心风险点
- 内部风险:数据库权限人员可直接查看用户明文密码;
- 外部风险:数据库被攻击后,攻击者获取明文账号密码,且用户常跨平台复用密码,导致多平台账号安全受威胁;
- 本质问题:密码无不可逆处理,泄露即等于原始密码暴露。
2. 错误实现示例(明文存储)
步骤1:编写创建用户函数(lib/user.js)
步骤2:调用创建用户函数(auth/actions.js)
二、正确方案:密码哈希存储
1. 核心原理
- 哈希特性:将密码通过哈希算法(如bcrypt)转换为不可逆的字符串,无法从哈希值还原原始密码;
- 验证逻辑:登录时将用户输入的密码重新哈希,与数据库中存储的哈希值比对,而非反向解密。
2. 正确实现示例(bcrypt哈希存储)
步骤1:安装依赖
步骤2:改造创建用户函数(lib/user.js)
三、关键总结
内容 | 错误做法 | 正确做法 |
密码存储形式 | 明文存储 | 哈希后存储(bcrypt等算法) |
安全核心 | 无保护,泄露即暴露密码 | 哈希不可逆,泄露也无法还原原始密码 |
验证逻辑 | 直接比对明文 | 输入密码哈希后与存储哈希比对 |
性能与安全平衡 | 无性能损耗,完全不安全 | 合理设置盐值轮数(如10),兼顾安全与性能 |
核心原则:永远不要在数据库中存储用户明文密码,必须通过不可逆哈希算法处理后再存储。
005 Hashing Passwords & Storing User Data The Right Way - 密码哈希与用户数据存储
一、核心安全原则
- 禁止明文存储密码:明文存储密码存在极高安全风险,一旦数据库泄露,用户账号将直接暴露,必须通过哈希处理将密码转换为不可逆的字符串后存储。
二、技术实现核心要素
1. 依赖文件与存储
- 关键文件:需引入
hash.js文件(依赖 Node.js 功能),建议存放于项目lib文件夹中,便于统一管理工具类代码。
- 文件核心功能:
- 功能1:将明文密码转换为不可逆哈希字符串(无法从哈希结果反推原始密码)。
- 功能2:提供密码校验函数,支持将用户登录时输入的密码与数据库中存储的哈希密码进行比对。
2. 密码校验逻辑(登录场景)
- 由于哈希算法不可逆,无法直接将数据库中的哈希密码转回明文。
- 校验流程:用户登录时输入明文密码 → 对输入密码重新执行相同哈希算法 → 对比新生成的哈希值与数据库中存储的哈希值 → 一致则验证通过。
3. 代码实践步骤(用户注册场景)
步骤 | 操作内容 | 关键说明 |
1 | 导入哈希函数 | 在 auth actions.js 文件中,从 hash.js 导入 hashUserPassword 函数 |
2 | 生成哈希密码 | 调用 hashUserPassword(明文密码),传入用户注册时输入的明文密码,生成哈希结果 |
3 | 存储哈希密码 | 将生成的哈希密码存入数据库,而非原始明文密码 |
三、关键作用与后续延伸
- 当前作用:该哈希逻辑是用户注册功能的核心安全环节,确保用户密码在存储环节的安全性。
- 后续关联:为后续用户登录功能提供密码校验基础,是完整用户认证流程(注册→登录→权限控制)的重要前置步骤。
006 Checking for Email Duplication - 用户注册邮箱重复校验笔记
核心背景
用户注册功能中,数据库用户表已配置邮箱唯一约束,需在前端/服务端处理“重复邮箱注册”的异常场景,避免数据库报错直接暴露,同时保证用户体验。
核心知识点
1. 异常捕获:try-catch 包裹数据库操作
核心逻辑
轻量级数据库交互包(Sequelize 生态)在违反唯一约束时会抛出错误,需通过 try-catch 捕获并针对性处理,而非直接抛出通用错误。
示例代码
2. 错误分类处理:区分“重复邮箱”与通用错误
核心逻辑
- 仅拦截“邮箱唯一约束”错误,返回友好提示;
- 其他错误(如数据库连接失败、字段验证错误)正常抛出,交由框架默认错误处理。
3. 成功态处理:页面重定向
核心逻辑
用户注册成功后,通过 Next.js 提供的
redirect 函数跳转到核心业务页面(而非停留在注册页)。4. 功能测试要点
测试场景 | 预期结果 |
输入无效短密码 | 前端展示密码格式错误提示 |
有效密码 + 未注册邮箱 | 注册成功并跳转到目标页面 |
有效密码 + 已注册邮箱 | 前端展示“邮箱已存在”提示 |
5. 功能局限与后续方向
- 当前仅完成“注册”流程,未实现身份验证(登录)功能;
- 缺少“验证请求是否来自已登录用户”的鉴权逻辑;
- 后续需补充登录接口、token 校验、权限控制等功能。
核心总结
- 数据库唯一约束需配合前端/服务端异常捕获,保证用户体验;
- try-catch 需精准区分错误类型,仅处理预期内的“重复邮箱”错误;
- 注册成功后通过 redirect 完成页面跳转,符合用户操作流程;
- 功能闭环需补充登录、鉴权等配套能力。
007 Theory How Does User Authentication Work - 用户身份验证工作原理
一、核心目标
确保未登录/未授权用户无法访问受保护资源(如
/training路由),仅允许通过验证的用户获取权限。二、身份验证两步核心流程
(一)第一步:用户登录与验证标记
- 客户端发起请求
- 用户访问网站后,填写身份验证表单(注册或登录),向服务器发送凭证(电子邮件+密码)。
- 服务器验证凭证
- 注册场景:检查电子邮件格式有效性、密码合规性,以及该用户是否已存在。
- 登录场景:比对提交的邮箱+密码组合与数据库中存储的用户数据是否匹配。
- 创建身份验证会话
- 若凭证有效,服务器在专用数据库表中创建会话条目,生成唯一且复杂的会话ID(难以被伪造)。
- 传递会话ID至客户端
- 服务器通过
cookie将会话ID返回给客户端,浏览器自动存储该“会话cookie”,完成用户“已验证”标记。
(二)第二步:已验证用户访问受保护资源
- 请求自动携带cookie
- 已登录用户访问受保护路由时,浏览器会自动将该网站的“会话cookie”附加到请求中,发送至服务器。
- 服务器验证会话有效性
- 服务器提取请求中的会话ID,检查其是否存在于数据库的会话表中,且处于活跃状态。
- 资源访问响应规则
- 有效会话:返回用户请求的资源(如页面内容)。
- 无效会话(如cookie错误、会话过期):返回错误,拒绝访问。
三、关键技术细节
- 会话存储:会话信息(含会话ID)存储在服务器数据库的专用表中,而非客户端本地。
- 会话ID特性:采用复杂字符串形式,避免被轻易生成或伪造,保障安全性。
- cookie作用:仅用于传递会话ID,不存储用户敏感信息,且由浏览器自动管理(存储、请求附加)。
四、后续学习方向
本视频讲解的身份验证流程,将在后续课程中通过代码实现(如基于Next.js 15框架搭建完整验证功能)。
008 Choosing a Third-Party Auth Package (Lucia) - 第三方身份验证包(Lucia)
一、核心背景:为何需要第三方身份验证库
- 身份验证流程涉及多步骤实现,手动开发难度高、易出错
- 第三方库可简化开发,降低重复编码成本,是行业常见实践
二、核心推荐工具:Lucia 身份验证库
1. 基本定位
- 功能:专注于简化身份验证流程的第三方库,支持会话管理、用户认证等核心能力
- 优势:设计简洁、使用门槛低,视频作者个人实践中“更直接、更易于使用”
2. 适用范围(跨环境兼容性)
兼容环境 | 说明 |
Next.js 应用 | 核心推荐搭配框架 |
其他 Node.js 应用 | 非 Next.js 场景也可使用 |
Bun Dino、Cloudflare Workers | 支持边缘计算等特殊环境 |
三、对比工具:NextAuth.js 的局限性(视频录制时现状)
- 兼容性问题:与 Next.js 的 App Router 不兼容,存在功能冲突
- 文档缺陷:文档不够完善,开发者学习和排查问题成本高
- 适用场景重叠:虽为 Next.js 设计,也可用于其他框架,但综合体验弱于 Lucia
四、Lucia 实战准备:安装与配置核心步骤
1. 安装命令
2. 关键配置逻辑
- 核心需求:Lucia 会自动创建用户会话,但需指定会话存储位置
- 解决方案:通过
@lucia-auth/adapter-sqlite包,将会话数据存储到 SQLite 数据库
- 后续操作:重启开发服务器,编写身份验证相关代码(视频后续将分步实现)
009 Creating a New Lucia Auth Instance - 创建Lucia Auth实例
一、前置基础
- 前提条件:已完成Lucia的安装,后续操作基于此开展用户认证功能开发
- 关联课程:属于Udemy《Next.js 15 & React - The Complete Guide 2024-10》课程的“09 - User Authentication”模块,是用户认证功能实现的关键步骤
二、核心操作步骤
(一)文件创建与依赖导入
- 创建文件:进入项目“唇文件夹”(视频表述,具体以实际项目结构为准),新建
auth.js文件,用于存放Lucia Auth实例相关配置
- 导入核心依赖
- 从
lucia包导入lucia:import lucia from 'lucia',作为创建Auth实例的基础 - 导入SQLite3适配器:从Lucia auth适配器相关包(如
@lucia-auth/adapter-sqlite3)中导入,用于对接SQLite3数据库 - 导入数据库句柄:从项目中已有的
db.js文件导入SQLite3数据库句柄(该文件中已创建并导出数据库实例),适配数据库操作
(二)配置SQLite3适配器
- 创建适配器对象:通过适配器构造函数构建,需传入两个关键参数
- 第一个参数:
db.js中导出的数据库句柄,建立Lucia与数据库的连接 - 第二个参数(配置对象):指定数据存储表名
userTable:设置为"users",告知Lucia用户数据存储在users表(需提前在db.js中创建该表)sessionTable:设置为"sessions",指定会话数据存储在sessions表
- 适配器作用:定义Lucia存储会话数据的方式,是连接Lucia与数据库的核心桥梁
(三)创建Lucia Auth实例
- 调用构造函数:
const lucia = lucia(adapter, config),接收两个核心参数 - 第一个参数:上述配置完成的SQLite3适配器对象,必填项
- 第二个参数(全局配置对象):用于自定义Lucia功能,重点配置会话Cookie
(四)会话Cookie关键配置
- 基础配置:在Next.js应用中,必须将
sessionCookie.expires设为false(遵循Lucia官方文档对Next.js的适配要求)
- 环境差异化配置:根据应用运行环境设置Cookie的HTTPS属性
- 逻辑判断:通过
process.env.NODE_ENV(多数托管平台自动配置)判断环境 - 生产环境(
NODE_ENV === "production"):强制sessionCookie.secure = true,仅允许Cookie在HTTPS协议下工作,保障数据安全 - 开发环境(本地运行):默认不强制HTTPS,方便开发调试
三、实例核心用途
- 会话管理:创建用户会话及对应的会话Cookie
- 请求验证:校验传入请求是否携带有效的会话Cookie,识别已认证用户
- 权限基础:为后续实现路由保护(拦截未认证用户访问)、用户身份识别等功能提供支撑
010 Configuring A Session & A Session Cookie
一、核心目标
在 Next.js 项目中,基于已初始化的 Lucia 库,实现“创建用户会话+设置会话 Cookie”的完整流程,为用户认证功能提供基础支持。
二、关键步骤
1. 定义“创建会话”异步函数
- 函数定位:在
off.js文件中导出,核心作用是为指定用户生成会话并关联 Cookie。
- 入参设计:接收用户信息(优先使用用户ID,而非电子邮件,更符合身份标识唯一性)。
- 核心功能:
- 为用户在会话数据库表中创建新条目(存储会话数据);
- 生成并设置与出站响应绑定的会话 Cookie。
2. 调用 Lucia 创建会话(核心逻辑)
- 调用语法:通过 Lucia 对象的创建会话方法,传入两个参数:
- 第一个参数:用户 ID(标识会话所属用户);
- 第二个参数:空对象
{}(若无需在会话中附加额外数据,如用户角色、权限等)。
- 底层机制:
- 该操作会返回 Promise,需用
await等待执行完成; - 执行成功后得到会话对象,包含系统自动生成的唯一会话 ID(会话的核心标识);
- 会话数据会自动写入预设的会话数据库表。
3. 获取会话 Cookie 配置数据
- 调用函数:通过 Lucia 提供的“创建会话 Cookie”函数,传入从会话对象中提取的
session.id。
- 返回结果:包含 Cookie 配置的对象,关键属性包括:
name:Cookie 名称;value:Cookie 值(核心为会话 ID);attributes:Cookie 附加配置(如secure、httpOnly等安全属性,由 Lucia 自动生成)。
4. 在 Next.js 中设置响应 Cookie
- 依赖工具:从
next/headers导入 Next.js 内置的cookies()函数,用于操作出站响应的 Cookie。
- 版本差异(关键注意点):
Next.js 版本 | 调用方式 | 说明 |
14 及更低版本 | 直接调用 cookies().set(...) | 无需 await,直接操作 Cookie |
15 及更高版本 | 需 await cookies() 后调用 set | 未来版本将强制要求使用 await,当前版本兼容两种方式 |
- 设置逻辑:调用
cookies().set(cookieName, cookieValue, cookieAttributes),直接传入从 Lucia 获取的 Cookie 配置对象属性,即可将会话 Cookie 附加到响应中。
三、后续操作与原理
- 浏览器行为:用户浏览器接收响应后,会自动存储会话 Cookie,后续请求会携带该 Cookie 以标识用户身份;
- 下一步动作:在代码中调用“创建认证会话”函数,触发会话创建流程(如用户登录成功后调用),完成认证初始化。
011 Setting Up An Auth Session - 授权会话(Auth Session)设置
一、核心定位:授权会话的创建时机与位置
- 触发场景:仅在用户完成登录或注册流程后创建授权会话
- 操作位置:位于注册服务器的操作函数中,执行时机为“重定向创建新会话”之前
二、技术实现步骤(基于Lucia工具)
- 依赖导入:从auth文件中导入“创建会话”的核心函数
- 用户ID获取:通过创建用户的返回结果,提取用户唯一ID(用户创建逻辑需预先配置“返回用户ID”)
- 会话创建执行:
- 将用户ID传入“创建授权会话”函数,该操作会生成Promise,需用
await等待完成 - 包裹在
try块中,若内部逻辑无异常,执行页面重定向,完成新用户会话创建
三、会话验证准备:环境重置
- 清除旧数据:删除训练用的
db数据库文件,避免旧用户数据干扰验证
- 重启服务:停止并重新启动开发服务器,自动生成新数据库,确保会话验证基于全新环境
四、会话结果验证:Cookie查看与机制确认
(1)查看会话Cookie
- 用有效邮箱+密码创建新用户,触发页面重定向
- 打开浏览器开发工具→进入应用程序选项卡→选择Cookies→定位
localhost:3000地址
- 找到Lucia生成的Cookie,其存储的值即为“会话ID”
(2)验证Cookie自动携带机制
- 进入浏览器开发工具-网络选项卡,刷新当前训练页面
- 查看页面首次初始请求的请求头,可观察到上述Cookie被自动附加
- 结论:浏览器会自动将该会话Cookie附加到所有出站请求中,无需手动配置
五、页面保护核心逻辑
- 保护目标:让页面仅对已登录用户可见
- 实现关键:检查请求中“授权Cookie”的存在性,并验证Cookie对应的会话ID有效性
六、后续开发规划
- 验证活跃授权会话的有效性
- 开发用户注销功能
- 完善用户登录功能(当前仅支持新用户注册时创建Cookie,登录功能待补充)
012 Verifying An Active Auth Session - 使用 Lucia 验证活跃授权会话
一、核心目标
确保请求来自已登录用户,不仅需判断授权 Cookie 存在,还需验证其有效性(避免伪造 Cookie),最终实现路由与页面的身份认证保护。
二、关键步骤与知识点
1. 定义验证函数(verify)
- 函数性质:异步函数(async),可自定义命名(示例为 verify),导出后供路由/页面调用。
- 核心作用:检查传入请求是否携带有效授权信息,返回包含“用户(user)”和“会话(session)”的对象,用于判断用户认证状态。
2. 提取请求中的会话 Cookie
- 提取方式:借助已有的
cookies函数 +get方法,结合lucia对象的session cookie name(提前配置的会话 Cookie 名称),从请求中获取会话 Cookie。
- 特殊情况:需考虑 Cookie 未定义的场景(即请求未携带会话 Cookie)。
3. 无有效 Cookie/会话 ID 的处理
- 判断条件:未获取到会话 Cookie,或 Cookie 中无有效会话 ID。
- 返回结果:返回
{ user: null, session: null }(也可根据需求返回false),明确标识请求来自未认证用户。
4. 验证会话 ID 有效性
- 核心方法:调用
lucia.validateSession(会话ID),该方法会查询数据库,验证两点: - 数据库中是否存在与该 ID 匹配的会话;
- 该会话是否仍处于有效状态。
- 结果结构:返回含
user和session的对象,有效会话则两者非空(含用户 ID、会话额外数据等),无效则为null。
5. 刷新有效会话与错误处理
- 刷新逻辑:若验证到有效活跃会话,且会话的
fresh属性为true,调用lucia.createSessionCookie(会话)重新创建 Cookie,延长会话有效期(避免用户被动登出)。
- Next.js 特殊处理:页面渲染时设置 Cookie 可能报错,需用
try-catch包裹操作并忽略该错误(参考 Lucia 官方文档)。
6. 清除无效会话 Cookie
- 触发场景:验证后未找到有效会话(Cookie 无效)。
- 操作方式:调用
lucia.createBlankSessionCookie()生成空白会话 Cookie,再通过cookies.set()设置,覆盖并清除原无效 Cookie。
7. 函数应用场景
- 适用范围:所有需“仅登录用户可访问”的路由(如个人中心、管理后台)和页面。
- 作用:作为权限控制的核心工具,判断请求是否通过认证,拦截未登录用户访问。
013 Protecting Routes Against Unauthenticated Access - 路由保护与认证
一、验证授权函数核心作用
- 适用范围:可用于任意需保护的页面(如培训页)或API路由,是实现访问控制的通用工具。
- 调用时机:在页面执行其他操作(如获取数据、渲染内容)前优先调用,避免未登录用户触发无效数据请求。
- 返回结果:返回包含
user和session键的对象,二者在无有效会话时均可能为空,需通过判断user是否存在识别用户认证状态。
二、未认证用户处理逻辑
- 判断条件:若验证授权函数返回的结果中无
user数据(即!result.user),判定为未认证用户。
- 核心操作:
- 不执行后续业务逻辑(如获取培训数据、渲染受保护页面);
- 借助
next navigation提供的重定向功能,强制将未认证用户导向起始页面,阻断对受保护路由的访问。
- 效果验证:
- 已登录状态:保存内容后刷新受保护页面,可正常访问;
- 删除认证Cookie:在浏览器开发者工具“应用程序- Cookie”中删除认证Cookie,刷新页面会自动重定向至起始页面(因请求无有效认证信息)。
三、当前功能现状与后续规划
- 现有功能局限:仅支持用户创建(注册),无用户登录和注销功能,认证体系不完整。
- 后续开发重点:
- 补充注销按钮,实现用户主动退出登录功能;
- 开发用户登录模块,支持现有账户登录,完善“注册-登录-注销”的完整认证流程。
014 Switching Auth Modes With Query Parameters (Search Params) - 认证模式切换(查询参数实现)
一、核心需求
实现用户在同一页面内无缝切换“登录”与“注册”模式,确保表单提交时能触发对应操作(避免仅默认触发注册)。
二、关键技术方案:查询参数(Search Params)管理模式
1. 方案选择理由
- 对比React的
useState钩子方案:useState仅在组件内部维护状态,刷新页面后状态丢失;而查询参数(如?mode=login)可持久化存储模式,刷新后仍能保持当前模式,且支持通过URL直接访问指定模式。
- 采用Next.js原生能力:利用Next.js自动为页面组件注入的
searchParams属性,无需额外配置即可提取URL中的查询参数。
2. 核心实现步骤
(1)提取查询参数并设置初始模式
- 操作文件:根页面的JS文件(如
page.js)。
- 关键代码逻辑:
- 通过对象解构获取
searchParams属性(Next.js内置,包含URL中所有查询参数); - 读取
searchParams.mode的值,若未设置(如首次访问页面),则默认设为“login”(登录模式); - 将模式通过
mode属性传递给认证表单组件(如<AuthForm mode={formMode} />)。
(2)定义表单组件的mode属性
- 类型约束:
mode为字符串类型,仅允许取值"login"(登录)或"register"(注册),确保参数合法性。
(3)根据mode动态渲染页面元素
渲染内容 | 逻辑判断 | 具体表现 |
切换链接 | mode === "login" | 显示“创建账户”链接,点击后跳转至当前页面并附加 ?mode=register查询参数 |
切换链接 | mode === "register" | 显示“使用现有账户登录”链接,点击后跳转至当前页面并附加 ?mode=login查询参数 |
表单按钮文本 | mode === "login" | 按钮显示“登录” |
表单按钮文本 | mode === "register" | 按钮显示“创建账户” |
三、后续计划
- 新增“登录”相关的服务器操作(Server Action);
- 基于
mode动态分配表单的服务器操作:当mode为“login”时,表单提交触发登录操作;当mode为“register”时,触发注册操作(当前仅实现模式切换,未关联操作逻辑)。
四、关键知识点总结
- Next.js 页面组件内置属性:
searchParams——用于提取URL中的查询参数,无需手动解析URL;
- 状态持久化方案:查询参数可实现跨刷新、跨会话的状态保存,适合“模式切换”这类需URL可访问的场景;
- 组件通信:通过
props将模式从页面组件传递给表单组件,确保数据流向清晰;
- 条件渲染:根据
mode动态调整UI,减少组件冗余,提升用户体验。
015 Adding User Login (via a Server Action) - 用户登录(Server Action 实现)
一、核心功能背景
- 所属课程:Udemy《Next.js 15 & React - The Complete Guide 2024-10》,对应“09 - User Authentication”模块的“015 Adding User Login (via a Server Action)”课时
- 核心目标:在 Next.js 15 项目中,通过服务器操作(Server Action) 实现用户登录功能,完成凭据验证、会话创建与页面重定向
- 前置依赖:需提前完成用户注册功能(含密码哈希存储)、Lucia 认证库配置(会话管理)、数据库用户表设计(含 ID、电子邮件、哈希密码字段)
二、关键实现步骤
1. 创建登录 Server Action 函数
- 文件位置:认证动作相关的 JS 文件(如
auth-actions.js)底部
- 函数定义:导出异步
login函数,需接收两个参数: prevState:表单之前的状态(用于维持表单状态管理)formData:表单提交数据(包含用户输入的电子邮件、密码)
- 核心逻辑:从
formData中提取email和password,后续用于数据库查询与验证
2. 实现数据库查询用户函数(getUser)
- 文件位置:
user.js(用户相关数据库操作文件)
- 函数功能:根据电子邮件从数据库查询用户数据
- 代码逻辑:
- 返回结果:找到用户则返回包含
id、email、hashedPassword的对象;未找到则返回undefined
3. 用户存在性验证与错误处理
- 查询用户:在
login函数中调用getUser(formData.get('email')),获取existingUser
- 错误场景:若
existingUser为undefined(无此用户),返回错误对象,与注册功能错误格式保持一致:
4. 密码有效性校验
- 依赖工具:
hash.js文件中的verifyPassword函数(提前实现,用于哈希密码比对)
- 校验逻辑:
- 入参:
existingUser.hashedPassword(数据库存储的哈希密码)、formData.get('password')(用户输入明文密码) - 核心原理:将用户输入密码重新哈希,与数据库存储的哈希值比对,返回布尔值
- 代码示例:
- 错误处理:若
isPasswordValid为false(密码不匹配),返回密码相关错误:
5. 创建用户会话与重定向
- 会话创建:若用户存在且密码验证通过,复用注册功能中的会话创建逻辑,使用
existingUser.id生成离线会话
- 核心代码(基于 Lucia 认证库):
- 页面重定向:会话创建成功后,重定向至用户主页或登录前页面(如
/dashboard)
- 组件关联:确保登录表单组件调用该
loginServer Action,绑定表单提交事件
6. 安全与功能校验
- 安全要点:不单独验证用户输入格式(如邮箱格式、密码长度),而是通过“是否查询到有效用户+密码是否匹配”间接验证,减少冗余逻辑
- 功能校验:确认
loginServer Action 已在登录表单组件中正确引用,确保表单状态与错误提示正常显示
016 Triggering Different Server Actions via Query Parameters - Next.js 15 服务器操作与用户认证
一、核心功能目标
通过“模式(mode)”参数区分登录/注册流程,用一个辅助服务器操作统一触发不同认证逻辑,同时完善登录状态显示与注销功能。
二、关键技术点:辅助服务器操作的实现
1. 新建“身份验证”辅助操作
- 文件位置:身份验证操作文件(如
auth-actions.js)
- 参数设计:需接收3个核心参数
mode:区分“登录”/“注册”的标识(关键触发条件)prevState:上一次的表单状态(用于表单回显、错误提示)formData:当前表单提交的数据(用户输入的账号、密码等)
- 逻辑判断:
2. 用 bind 方法预配置“模式”参数
- 问题背景:辅助操作需
mode参数,但表单提交时默认无法自动传递
- 解决方案:利用 JavaScript 原生
bind方法预配置参数 bind第一个参数:设为null(不关注this指向,因服务器操作中无需绑定上下文)bind后续参数:传入“登录”或“注册”模式(如'login'/'register'),会作为固定参数传递给authenticate操作
- 使用示例:
三、功能验证与效果
测试场景 | 预期结果 |
登录模式-无效用户 | 提交后显示错误(无匹配用户) |
登录模式-有效用户 | 1. 成功设置 auth Cookie(存储登录状态)<br>2. 自动重定向到目标页面(如培训页) |
注册模式(隐含验证) | 调用注册逻辑,完成用户创建与状态返回 |
四、登录状态扩展:顶部标题与注销功能
1. 登录状态下的 UI 显示
- 需求:用户登录后,页面顶部显示包含“退出”按钮的标题
- 核心逻辑:通过判断
authCookie 是否存在(或会话是否有效),动态渲染标题内容
2. 注销功能实现
- 核心操作:点击“退出”按钮时,清除存储登录状态的
authCookie
- 效果:Cookie 清除后,用户会话失效,跳转回未登录状态(如登录/注册页面)
五、关键思路总结
- 统一入口:用一个辅助服务器操作(
authenticate)管理多个认证逻辑(登录/注册),减少代码冗余
- 参数预配置:
bind方法是传递固定参数的关键,解决服务器操作与表单交互时的参数缺失问题
- 状态持久化:通过
authCookie 维护登录状态,确保页面刷新后状态不丢失
- 流程闭环:覆盖“触发操作→功能验证→状态显示→注销清理”全流程,确保认证功能完整可用
017 Adding an Auth-only Layout
一、核心需求与方案选择
- 需求场景:需添加仅对已登录用户可见的标题(含欢迎信息+注销按钮),且该标题需应用于多个需登录访问的页面,同时避免在登录页显示注销按钮
- 方案排除:不使用根布局(会导致注销按钮在所有页面,包括登录页显示)
- 最优方案:通过路由组(Route Groups) 为需用户验证的页面创建专属布局(Auth-only Layout)
二、关键技术:路由组(Route Groups)
- 创建规则:
- 用括号包裹文件夹名称(如
(auth-pages)),括号内自定义命名 - 将所有需登录访问的页面移入该文件夹
- 核心特性:
- 不向 URL 路径添加新段,仅用于页面逻辑分组
- 可单独为该组页面设置统一布局,不影响其他页面
三、Auth-only Layout 实现步骤
1. 基础结构搭建
2. 关键细节处理
- CSS路径调整:路由组文件夹虽不影响URL,但属于文件系统中的物理文件夹,布局文件中导入CSS需用
../向上一级查找(如原./globals.css改为../globals.css)
- 样式应用:为头部添加
Auth-titleid,关联预设CSS样式,确保视觉效果
- 功能待完善:注销按钮表单暂无法工作,需后续结合认证逻辑(如Lucia Auth)实现点击事件
四、核心原理与优势
- 布局优先级:路由组布局与根布局同级,会替换根布局在该组页面中的作用,实现“分组专属布局”效果
- 复用性:一次配置,自动应用于路由组内所有页面,无需在每个页面重复编写登录用户头部
- 路由隔离:未加入路由组的页面(如登录页)不受影响,避免注销按钮误显示问题
018 One Root Layout vs Multiple Root Layouts
In the previous lecture, we added the
(auth) route group and an auth-only layout (by adding a layout.js file in that (auth) folder).在上一讲中,我们添加了(auth)路由组和一个仅限授权的布局(通过在该(auth)文件夹中添加一个layout.js文件)。Currently, this
(auth)/layout.js layout will actually not replace the main root layout (i.e., the layout provided by app/layout.js).目前,这个(auth)/layout.js布局实际上不会替换主根布局(即由app/layout.js提供的布局)。Because route group layouts are actually nested into the main root layout unless there is no such root layout. I.e., if you would create another route group (e.g.,
(unauth)) and move the layout.js file into that folder, you would end up with multiple root layouts.因为路由组布局实际上会嵌套到主根布局中,除非不存在这样的根布局。也就是说,如果你创建另一个路由组(例如(unauth))并将layout.js文件移动到该文件夹中,你最终会得到多个根布局。Therefore, if you're not doing that, you should edit the
(auth)/layout.js file and remove the <html> and <body> elements in there. The app will work either way but it's technically incorrect.因此,如果你没有这样做,你应该编辑(auth)/layout.js文件,并删除其中的<html>和<body>元素。无论哪种方式,应用程序都能运行,但从技术上讲这是不正确的。Instead, your
(auth)/layout.js file should look like this:相反,你的(auth)/layout.js文件应该是这样的:019 Adding User Logout - Lucia 实现用户注销功能
一、核心目标
在 Next.js 项目中,基于 Lucia 身份验证库实现用户注销功能,核心是销毁用户当前会话、清除会话 Cookie,并将用户重定向至登录页,同时确保注销后未授权用户无法访问受保护页面。
二、关键文件与核心函数
文件名 | 核心作用 | 关键函数/操作 |
auth actions.js | 定义注销相关服务器操作 | 导出 logout 异步函数,调用销毁会话逻辑并实现页面重定向 |
auth.js | 封装 Lucia 身份验证核心逻辑 | 新增销毁会话函数,负责验证会话有效性、删除数据库会话、清除会话 Cookie |
三、核心步骤拆解
1. 编写「销毁会话函数」(在 auth.js 中)
(1)验证会话有效性
- 调用 Lucia 的
verify函数(返回 Promise),获取包含user和session的结果对象;
- 若未检测到
session(无会话 Cookie),返回含「未授权」错误的对象(或抛出错误),终止流程;
- 若存在有效
session,进入下一步。
(2)销毁数据库会话
- 调用 Lucia 的
invalidateSession(session.id)方法,与数据库会话表通信,删除当前用户的会话记录,实现“系统忘记用户登录状态”。
(3)清除会话 Cookie
- 设置空白的会话 Cookie(覆盖原 Cookie),确保客户端 Cookie 被有效删除,彻底终止客户端登录状态。
2. 编写「注销操作函数」(在 auth actions.js 中)
- 导出
logout异步函数,内部调用auth.js中定义的「销毁会话函数」并等待执行完成;
- 执行重定向逻辑,使用 Next.js 相关方法将用户跳转至登录页(或项目起始页)。
3. 绑定注销操作到页面(Auth 布局中)
- 在需要显示注销按钮的 Auth 专属布局中,导入
auth actions.js的logout函数;
- 将
logout操作绑定到注销按钮所在的表单(或点击事件),确保点击按钮时触发注销流程。
四、功能验证与效果
- 注销触发:登录状态下点击注销按钮,触发
logout函数,执行会话销毁+Cookie 清除+页面重定向;
- 权限控制:注销后尝试访问受保护页面(如
/slash training),会因无有效会话持续重定向至登录页;
- 可重复性:重新登录后可再次执行注销操作,功能逻辑稳定。
📎 参考文章
- 一些引用
- 引用文章
欢迎您在底部评论区留言,一起交流~
Loading...
