免责声明:本文为个人技术学习与工程实践笔记,所涉操作仅应在获得授权的环境中进行。因不当使用造成的后果由使用者自行承担。
在 Nuxt 3 中,路由守卫常被用于控制页面访问权限,例如判断用户是否登录、校验 JWT 权限,或在路由跳转前执行特定逻辑。从 Angular 与 Next.js 迁移过来的开发者会发现,Nuxt 3 提供了多种实现方式,包括中间件(Middleware)、Vue Router 钩子以及组件内的生命周期函数。
这些机制在前端体验层面足够灵活,但它们的执行位置决定了它们并不构成安全边界:路由守卫运行在用户完全可控的运行时环境中,它只能决定「当前浏览器是否渲染某个页面」。本文先梳理几种常见写法,再说明围绕这些写法容易产生的认知误区,最后给出把鉴权落实到服务端的加固方式。
1.通过 Middleware 管理路由访问
在 Nuxt 3 中,推荐使用中间件来处理页面访问控制,中间件可以在页面渲染之前拦截路由请求,并执行自定义逻辑
比如我们先创建一个 auth.js
1 | // middleware/auth.js |
然后再页面或者布局中应用中间件,比如在 SPA 下
1 | <script setup> |
布局应用中间件
1 | <script setup> |
全局中间件,如果想要在全局页面生肖可以在 nuxt.config.ts 设置
1 | export default defineNuxtConfig({ |
2. 使用 Vue Router 的 beforeEach 钩子
Nuxt 3 默认使用 Vue Router,因此可以直接利用 Vue Router 的全局导航守卫进行控制
1 | <script setup> |
这里每次路由切换时都会触发,可以处理登录验证、权限控制或其他自定义逻辑
3. 页面组件中的路由守卫
在 Vue 组件中,还可以用 onBeforeRouteLeave 和 onBeforeRouteUpdate 钩子处理路由变化
1 | <script setup> |
说明:
onBeforeRouteLeave:用于阻止用户离开页面(如表单未保存提示)onBeforeRouteUpdate:用于同一路由下参数变化时的逻辑处理
4. 授权检查示例
1 | export default defineNuxtRouteMiddleware((to) => { |
常见认知误区
上述写法都可以正常工作,问题出在对它们的能力边界理解不准。以下几类误区在实践中反复出现。
把前端路由守卫当作访问控制边界
配置 middleware: 'auth' 之后,未登录用户确实无法在前端打开目标路由,但这只是阻止了页面渲染。守卫的判断依据取自客户端可读写的位置——cookie、localStorage 或内存中的状态——因此判断本身可以被篡改:只要改动判断依据,守卫就会放行。
真正决定数据能否被访问的,是后端接口是否对每一次请求校验身份与权限。如果接口仅依赖请求参数判断数据归属,未授权请求依然会返回数据;换言之,绕过前端页面并不等于绕过鉴权,反之亦然。评估一套系统时,应以接口的实际响应为准,而不是以页面能否打开为准。
把身份信息存放在客户端可读写的位置
useCookie('token') 与 useState('currentUser') 常被直接用作登录态载体,但两者都不适合作为可信来源:未设置 httpOnly 的 cookie 对脚本完全可读写;useState 是 SSR 请求内的共享状态,页面刷新后不会保留。将角色、权限等授权信息保存在 localStorage 中同样如此,服务端不应采信客户端上报的布尔值或角色字段。
正确的做法是由服务端签发并校验登录态(签名令牌或服务端会话),鉴权逻辑只信任自己验证过的凭据。此外,令牌存放于脚本可读写的位置会放大 XSS 的影响面,因此推荐 httpOnly + Secure + SameSite 的 cookie 组合,并配套 CSRF 防护。
重定向目标未做白名单校验
为了登录后回跳,开发者有时会直接使用 query 参数中的地址,例如 return navigateTo(to.query.redirect)。该参数一旦缺少校验,攻击者就可以构造一个指向外部站点的登录链接,使用户在完成登录后被带到非预期站点,形成开放重定向。在钓鱼场景中,可信域名下的链接会显著提高诱导成功率。
加固方式是对回跳地址做白名单匹配:只允许站内相对路径,或对允许的域名做显式枚举,其余情况一律回落到默认页面。
守卫逻辑自身形成跳转环路
如果守卫的放行条件与登录页自身不匹配,例如未登录时无条件跳转到 /login,而 /login 也命中同一条守卫,就会形成不断重复的跳转:
1 | /login -> /login -> /login |
在服务端渲染场景下,每次跳转都会消耗一次渲染资源,持续的请求会放大为服务端压力。加固要点是让白名单与守卫条件互补,登录、注册、错误页等无需鉴权的路由必须显式排除。
过度信任 query 与 params
路由参数最终往往会被渲染到页面上。若在模板中使用 v-html,或向 dangerouslySetInnerHTML 传入未净化的内容,参数中的标记就会被当作 HTML 解析,形成 XSS;v-html 是其中最常见的入口。Vue 的默认插值会对内容做转义,风险集中出现在主动跳过转义的写法上。加固方式是尽量避免渲染富文本,必须渲染时使用白名单化的净化库,并配置 CSP 限制内联脚本。
加固建议
前端路由守卫的定位应当明确:它用于改善交互体验、减少无效请求,不承担访问控制职责。围绕这一点,可以按以下层次收敛风险。
- 服务端为唯一的授权决策点。所有数据与操作接口(Nuxt 的
server/api、server/routes或独立后端服务)都要在服务端完成身份校验与对象级权限判断,不依赖前端传入的角色或状态字段;页面守卫只负责跳转与提示。 - 登录态设计。会话令牌由服务端签发与校验,cookie 设置
httpOnly、Secure、SameSite,并配合 CSRF token;避免把角色、权限等授权信息保存到 localStorage。 - 跳转与参数处理。回跳地址走白名单;路由参数在渲染前做类型与范围校验;富文本渲染使用净化库。
- 分层防护。配置 CSP 降低 XSS 的可用性;对服务端渲染的路由做频率限制与超时控制,避免跳转环路被用于资源耗尽。
- 上线前排查清单。逐项确认:页面守卫是否被当作唯一鉴权手段;接口对未登录与越权访问是否返回 401/403;cookie 属性是否齐全;是否存在开放重定向;服务端渲染路径是否有速率限制与超时。