Common Misconceptions and Hardening Practices in Nuxt 3 Front-End Route Authorization

免责声明:本文为个人技术学习与工程实践笔记,所涉操作仅应在获得授权的环境中进行。因不当使用造成的后果由使用者自行承担。

在 Nuxt 3 中,路由守卫常被用于控制页面访问权限,例如判断用户是否登录、校验 JWT 权限,或在路由跳转前执行特定逻辑。从 Angular 与 Next.js 迁移过来的开发者会发现,Nuxt 3 提供了多种实现方式,包括中间件(Middleware)、Vue Router 钩子以及组件内的生命周期函数。

这些机制在前端体验层面足够灵活,但它们的执行位置决定了它们并不构成安全边界:路由守卫运行在用户完全可控的运行时环境中,它只能决定「当前浏览器是否渲染某个页面」。本文先梳理几种常见写法,再说明围绕这些写法容易产生的认知误区,最后给出把鉴权落实到服务端的加固方式。

1.通过 Middleware 管理路由访问

在 Nuxt 3 中,推荐使用中间件来处理页面访问控制,中间件可以在页面渲染之前拦截路由请求,并执行自定义逻辑

比如我们先创建一个 auth.js

1
2
3
4
5
6
7
8
9
10
11
12
// middleware/auth.js
export default defineNuxtRouteMiddleware((to) => {
// 登录不需要校验
const openRoutes = ['/login', '/register']
// 如果在白名单内,直接通过
if (openRoutes.includes(to.path)) return
// 检查 token
const token = useCookie('token').value
if (!token) {
return navigateTo('/login')
}
})

然后再页面或者布局中应用中间件,比如在 SPA 下

1
2
3
4
5
6
7
<script setup>
const pageGuard = 'auth'
// 配置页面元信息
definePageMeta(() => ({
middleware: pageGuard
}))
</script>

布局应用中间件

1
2
3
4
5
<script setup>
defineLayoutMeta({
middleware: 'auth' // 所有使用该布局的页面都会生效
})
</script>

全局中间件,如果想要在全局页面生肖可以在 nuxt.config.ts 设置

1
2
3
4
5
export default defineNuxtConfig({
router: {
middleware: ['auth'] // 全局应用
}
})

2. 使用 Vue Router 的 beforeEach 钩子

Nuxt 3 默认使用 Vue Router,因此可以直接利用 Vue Router 的全局导航守卫进行控制

1
2
3
4
5
6
7
8
9
10
11
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
router.beforeEach((to, from) => {
const currentUser = useState('currentUser')
// 如果未登录并且不是去登录页,强制跳转
if (!currentUser.value && to.name !== 'login') {
return { name: 'login' }
}
})
</script>

这里每次路由切换时都会触发,可以处理登录验证、权限控制或其他自定义逻辑

3. 页面组件中的路由守卫

在 Vue 组件中,还可以用 onBeforeRouteLeave 和 onBeforeRouteUpdate 钩子处理路由变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 离开当前页面前执行
onBeforeRouteLeave((to, from) => {
if (confirm('确定要离开当前页面吗?未保存的数据将丢失')) {
return true
} else {
return false
}
})
// 当同一路由参数更新时触发
onBeforeRouteUpdate((to, from) => {
console.log('路由参数已变更:', to.params)
})
</script>

说明:

  • onBeforeRouteLeave:用于阻止用户离开页面(如表单未保存提示)
  • onBeforeRouteUpdate:用于同一路由下参数变化时的逻辑处理

4. 授权检查示例

1
2
3
4
5
6
export default defineNuxtRouteMiddleware((to) => {
const currentUser = useState('currentUser')
if (!currentUser.value && to.name !== 'login') {
return navigateTo('/login')
}
})

常见认知误区

上述写法都可以正常工作,问题出在对它们的能力边界理解不准。以下几类误区在实践中反复出现。

把前端路由守卫当作访问控制边界

配置 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 限制内联脚本。

加固建议

前端路由守卫的定位应当明确:它用于改善交互体验、减少无效请求,不承担访问控制职责。围绕这一点,可以按以下层次收敛风险。

  1. 服务端为唯一的授权决策点。所有数据与操作接口(Nuxt 的 server/api、server/routes 或独立后端服务)都要在服务端完成身份校验与对象级权限判断,不依赖前端传入的角色或状态字段;页面守卫只负责跳转与提示。
  2. 登录态设计。会话令牌由服务端签发与校验,cookie 设置 httpOnly、Secure、SameSite,并配合 CSRF token;避免把角色、权限等授权信息保存到 localStorage。
  3. 跳转与参数处理。回跳地址走白名单;路由参数在渲染前做类型与范围校验;富文本渲染使用净化库。
  4. 分层防护。配置 CSP 降低 XSS 的可用性;对服务端渲染的路由做频率限制与超时控制,避免跳转环路被用于资源耗尽。
  5. 上线前排查清单。逐项确认:页面守卫是否被当作唯一鉴权手段;接口对未登录与越权访问是否返回 401/403;cookie 属性是否齐全;是否存在开放重定向;服务端渲染路径是否有速率限制与超时。

References

Support via Solana

Solana

Solana

Solana Pay

Solana Pay

WeChat

WeChat