H3 会话基础
会话(session)是用 cookie 记住用户状态的标准做法:登录态、语言偏好、购物车,都靠它在多次请求之间保持。H3 把会话封装成一组工具,其中你 90% 的时间只用 useSession。
工具家族
useSession(event, opts):初始化(或取出)会话,返回一个可操作的包装对象。最常用。getSession(event, opts):只取当前会话,不创建新的。updateSession(event, opts, data):更新会话数据。clearSession(event, opts):清空当前会话。
useSession 在底层就是「没有就创建、有就取出」的 getSession + 写回逻辑,所以下面所有示例都用它。
初始化一个会话
import { useSession } from "h3";
app.use(async (event) => {
// password 是封印每个会话 cookie 的密钥,必须从环境变量加载(见下文安全警告)
const session = await useSession(event, {
password: process.env.SESSION_PASSWORD ?? "[演示占位符,生产环境务必替换为随机密钥]",
});
// session 已就绪,可读取或写入 session.data
return session.data;
});
初始化成功后,响应里会带上一个 Set-Cookie,cookie 名默认是 h3,内容是加密后的会话数据。如果后续请求带着名为 h3 的 cookie、或名为 x-h3-session 的请求头,H3 就会据此还原会话。
💡 cookie 与 header 二选一:
x-h3-session请求头优先于 cookie。这意味着你可以把会话通过 header 传递(例如在服务端之间转发、或某些不允许用 cookie 的运行环境),不必依赖浏览器自动带 cookie。
读取会话数据
数据存在 session.data 上,没有数据时是个空对象:
import { useSession } from "h3";
app.use(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_PASSWORD ?? "[演示占位符]",
});
return session.data; // 可能是 {},也可能是之前写入的内容
});
写入:自增访问次数
import { useSession } from "h3";
app.use(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_PASSWORD ?? "[演示占位符]",
});
// 先读旧值(没有就当 0),加一,再写回
const count = (session.data.count ?? 0) + 1;
await session.update({ count }); // 底层调用 updateSession
return count === 1
? "欢迎首次访问!"
: `你已访问本页 ${count} 次`;
});
逻辑是:取出会话 → 没有就新建 → 给 count 加一 → 写回。多访问几次,计数器就会累加。
⚠️ 用
curl这类不保存 cookie 的命令行工具测试时,你会看到计数一直是 1——因为它每次都是新会话。要把响应里的Set-Cookie取出来、下次用--cookie回传,才能模拟浏览器的行为。
销毁会话
import { useSession } from "h3";
app.use("/clear", async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_PASSWORD ?? "[演示占位符]",
});
await session.clear(); // 底层调用 clearSession
return "会话已清除";
});
clear() 会让 H3 发送一个值为空、过期为 0 的 h3 cookie,浏览器据此删掉它。
安全警告:password 的熵才是真正的边界
⚠️
password用于封印(seal)每一个会话 cookie,它的熵(随机性)才是真正的安全边界。被窃取的会话 cookie 里,盐值和完整性摘要都是明文携带的——也就是说,拿到 cookie 的人可以离线暴力破解你的password。提高 PBKDF2 迭代次数只是拖慢破解,并不能修复一个低熵的密钥。
永远用密码学安全的随机源生成它,例如:
sh
# 生成 32 字节随机 base64 串,作为会话封印密钥
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64'))"
下面示例为了可读用了硬编码值,但真实项目必须从环境变量(如process.env.SESSION_PASSWORD)加载一个至少 32 字符的随机密钥,且绝不能提交进源码仓库。一个可被猜到的口令(哪怕长度 ≥32)同样不安全。

TRAE-AI编程


更多建议: