服务端状态与跨请求安全
本教程共 50 篇 · 第 28 篇 · 更新于 2026-08-08 · 约 8 分钟阅读
本节目标:理解 Nuxt 服务端「每个请求彼此隔离」的道理,掌握 cookie/会话与请求级状态的正确写法,避免数据串号。
28-1
前面几章都在聊浏览器端的状态(Pinia、useState)。这一章翻到服务端:那些放在 server/ 目录下的 API 和中间件。它们由 Nuxt 内置的 Nitro 引擎驱动,底层用的是轻量 HTTP 框架 h3。
一个最关键的认知:服务端是「多请求并发」的环境。你的应用上线后,同一时刻可能有一百个用户各发来一个请求。服务器会为每个请求跑一遍你的 handler 代码。所以「状态放在哪、会不会被别的请求看到」,直接关系到安全。
Warning千万不要在
server/目录的文件顶层写const userState = {}这种「模块级全局变量」来存请求相关的数据。在长时间运行的服务(或 serverless 实例复用)里,这个对象会被后续所有请求共享,上一个用户的数据会泄漏给下一个用户——这是典型的安全事故。
28-2
h3 的每个请求都会拿到一个 event 对象(类型是 H3Event)。它是「本次请求」的专属信封,里面装着请求信息、响应方法,还可以挂你自己的上下文。正确做法是:把只在「这一次请求内」共享的数据,挂在 event.context 上。
export default defineEventHandler((event) => {
// 本次请求内,多个 server 函数间共享的数据,挂在这里
event.context.userId = '123'
return { hello: 'world' }
})
event.context 是「请求级」的:每个请求各自一份,互不干扰,请求结束就作废。这正是跨请求安全的核心——请求相关的状态,永远随请求走,不进模块全局。
Note在 h3 里,
event.context就是专门给你挂「本次请求上下文」的地方。Server 中间件里算好的东西(比如解析出的用户身份),挂到event.context,后面的 handler 就能直接读,而不必每个 handler 重复解析。
28-3
服务端中间件(server/middleware/)会在每个请求进来时先跑一遍,很适合在这里解析 token、识别用户,并把结果挂到 event.context,供后续 handler 使用。
export default defineEventHandler((event) => {
const token = getCookie(event, 'token')
if (token) {
// 伪代码:解析 token 得到用户
event.context.user = parseToken(token)
}
})
export default defineEventHandler((event) => {
// 直接读中间件挂好的上下文
return { user: event.context.user ?? null }
})
这样「鉴权」只写一次,所有接口都能受益。
Warning服务端中间件如果
return了一个值,Nitro 会把它当作响应直接返回、终止后续处理。所以鉴权类中间件不要返回值,只往event.context上挂数据即可;要拦截请求(如未登录跳转),用throw createError(...)或sendRedirect。
28-4
cookie 是服务端识别「这是哪个用户」最常用的手段。h3 提供了一组读/写 cookie 的辅助函数,比原生 event.node.req.headers.cookie 好用得多。
读 cookie:
export default defineEventHandler((event) => {
const token = getCookie(event, 'token')
return { hasToken: !!token }
})
写 cookie(比如登录成功后下发会话):
export default defineEventHandler(async (event) => {
const { username, password } = await readBody(event)
// 伪代码:校验凭据
const sessionId = createSession(username)
setCookie(event, 'sid', sessionId, {
httpOnly: true,
sameSite: 'lax',
path: '/',
})
return { ok: true }
})
httpOnly: true 让前端 JS 读不到这个 cookie,能挡掉大部分 XSS 偷 cookie 的攻击;sameSite 有助于防御 CSRF。这些安全选项,生产环境请务必带上。
28-5
前面第 22 章提过:在组件里用 useFetch 调相对路径时,Nuxt 会经 useRequestFetch 自动把浏览器的 cookie 和请求头代理到这次服务端请求(除了 host 这类不该转发的)。
<script setup lang="ts">
// 服务器上执行时,会自动带上用户浏览器的 cookie 去请求 /api/me
const { data } = await useFetch('/api/me')
</script>
如果你不用 useFetch,而是自己 $fetch,又想带上 cookie,就得手动用 useRequestHeaders(['cookie']):
<script setup lang="ts">
const headers = useRequestHeaders(['cookie'])
async function getCurrentUser () {
return await $fetch('/api/me', { headers })
}
</script>
Warning把浏览器请求头代理给外部 API 时要格外小心,只转发你确实需要的头。像
host、accept、content-length、x-forwarded-*、cf-connecting-ip这类头不适合转发,乱转发可能引入安全隐患或奇怪的行为。
28-6
一个铁律:密码、API 密钥、数据库凭证这类东西,永远只在服务端出现。在 Nuxt 里,把它们放进 runtimeConfig 的 private 段(或环境变量 .env),只在 server/ 代码里读取;绝不要写进客户端组件,也不要通过 API 原样返回给浏览器。
export default defineEventHandler((event) => {
// 只在服务端读私密配置,安全
const apiKey = useRuntimeConfig(event).secretApiKey
return { ok: true }
})
Tip服务器 handler 里
return的对象会直接变成 JSON 响应;只return你想公开的数据。任何敏感字段,要么不返回,要么先脱敏。记住:只有你显式return的东西才会离开服务器,其余都安全留在服务端。
28-7
和浏览器端不同,服务端代码是「来一个请求、执行一遍、返回、结束」,没有 Vue 那套响应式系统,也没有组件挂载/卸载。所以:
- 不要在 handler 顶层开
setInterval/setTimeout去「持有状态」,请求结束它们还在跑,且会看到过期数据。 - 不要在模块顶层存可变的请求数据(前面强调过,会串号)。
- 需要「跨请求共享但只读」的东西(如配置、连接池),才适合放模块级常量——且必须是不可变或自身线程安全的。
28-8
服务端机制在 Nuxt 3 与 Nuxt 4 中一致,均基于 Nitro + h3。仅目录位置不同:Nuxt 4 用 server/(与 app/ 平级),Nuxt 3 也是 server/,两者在此处相同;Nuxt 3 老项目若用 runtimeConfig,字段结构与 Nuxt 4 一致,private / public 的划分相同。
28-9
服务端状态管理的核心就一句话:请求相关的数据随请求走,挂在 event.context 上,绝不进模块全局。 用 getCookie / setCookie 处理会话,用 runtimeConfig 保管密钥,用中间件统一鉴权。守住「隔离」与「不泄密」两条线,你的 Nuxt 全栈应用才既好用又安全。至此,「数据与状态」这一部分(第 22–28 章)就讲完了,下一部分我们会进入样式与静态资源。
28-6 跨请求安全的实践清单
保障跨请求安全不需要复杂的架构,但需要养成良好的编码习惯。以下是一份简明的实践清单,帮你在日常开发中避免常见的安全陷阱。
首先,永远不要在模块顶层用普通变量存储请求相关的数据。任何在 <script setup> 外、模块作用域里声明的变量,都可能在多个请求间共享。其次,使用 Nuxt 提供的 useState、useCookie、useRequestHeaders 等组合式函数来管理请求级别的状态,它们会自动处理跨请求隔离。
第三,在编写服务端中间件或 API 路由时,注意从 event 对象获取上下文信息,而不是依赖全局状态。第四,定期审查项目中使用 globalThis 或进程级缓存的代码,确认它们不会泄漏用户数据。遵循这些原则,你的 Nuxt 应用就能在多用户并发访问时保持安全。
28-7 安全编码的检查清单
编写服务端安全的代码需要养成一些习惯。以下是一份简明的检查清单,帮你在日常开发中避免常见的安全问题。
第一,永远不要信任客户端传来的数据。所有来自请求参数、请求体、请求头的值都应该经过验证和清理后再使用。第二,避免在响应里返回不必要的信息。错误消息不要包含堆栈跟踪或系统路径,只返回用户需要知道的提示。
第三,敏感操作(如删除数据、修改密码)要用 POST 或 DELETE 方法,不要用 GET。GET 请求会被浏览器预加载、被搜索引擎爬虫访问,可能触发意外的操作。第四,定期更新依赖版本,修复已知的安全漏洞。Nuxt 和 Nitro 团队会及时发布安全补丁,保持更新是最基本的安全措施。