You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

6.3 KiB

权限与访问控制设计(Nuxt)

背景与目标

当前项目已有基础账号体系与会话能力(登录、注册、获取当前用户),但缺少统一的页面与 API 访问控制策略。
本设计目标:

  • 采用登录优先:默认受保护,少量白名单放行
  • 保持实现简洁、易维护、可扩展
  • 避免常见安全问题(越权、开放重定向、误放行、重定向循环)
  • 支持“同一路由按登录态展示不同内容”的页面模式

需求结论(已确认)

  • 白名单配置方式:代码静态配置
  • 页面策略:默认拦截,白名单放行
  • 未登录白名单页面://login/register,并预留认证辅助页扩展
  • 页面展示模式:混合模式
    • 入口页(如 /)同路由按登录态切换
    • 复杂业务页拆分为受保护路由
  • 未登录访问受保护页面:跳转 /login?redirect=...,登录后回跳
  • API 策略:默认要求登录,仅少数白名单 API 放行

总体方案(方案 1)

采用“双层守卫”:

  1. 前端全局路由中间件(页面守卫)
  2. 服务端 API 中间件(接口守卫)

两层共同保证:即使前端绕过,后端仍可阻断未授权调用。

架构设计

1) 前端会话状态层

新增统一会话组合式函数(例如 app/composables/useAuthSession.ts):

  • 负责请求 /api/auth/me
  • 暴露 loggedInuserpendingrefreshclear
  • 登录成功后调用 refresh
  • 登出或 401 时调用 clear

设计原则:

  • 不在多个页面重复写“是否登录”逻辑
  • 统一处理会话失效状态

2) 页面守卫层(全局)

新增 app/middleware/auth.global.ts,策略如下:

  • 默认:页面需要登录
  • 放行白名单://login/register 及后续认证辅助页(代码增补)
  • 未登录访问受保护页:
    • 跳转 /login?redirect=<当前完整路径>
  • 已登录访问游客页(如登录/注册页):
    • 优先跳 redirect
    • 无合法 redirect 则跳默认登录后落地页(初始可设为 /

3) API 守卫层(服务端)

新增 server/middleware/10.auth-guard.ts,策略如下:

  • 仅处理 /api/**
  • 默认要求登录
  • API 白名单(初始):
    • /api/auth/login
    • /api/auth/register
    • 必要公开读取接口(如 /api/config/global 的 GET)
  • 未登录或无效会话:统一返回 401

补充:

  • 登录态来源统一走 event.context.auth.getCurrent()(沿用现有上下文能力)
  • 不在错误响应中暴露内部实现细节

关键安全设计

1) redirect 安全校验(防开放重定向)

redirect 仅接受站内相对路径,校验规则:

  • 必须以 / 开头
  • 禁止 // 开头
  • 禁止包含协议(如 http:https:javascript:
  • 非法值全部降级为默认落地页

2) 白名单匹配规则(防误放行)

采用“精确匹配 + 明确前缀匹配”:

  • 精确:/login
  • 前缀:/auth/forgot-password(示例)

禁止宽泛正则(如 ^/a 这类容易误伤或放大权限边界的规则)。

3) 未授权语义统一

  • API 统一返回 401
  • 页面统一重定向到登录页
  • 前端收到 401 后同步清理本地会话状态,避免脏态

4) 防重定向循环

  • 登录/注册页不再跳自身
  • redirect 指向游客页本身时,降级到默认落地页

代码组织建议

建议新增/调整文件:

  • app/composables/useAuthSession.ts:会话读取与缓存
  • app/middleware/auth.global.ts:页面访问控制
  • app/utils/auth-routes.ts:页面白名单、游客页集合、redirect 校验工具
  • server/middleware/10.auth-guard.ts:API 访问控制
  • server/utils/auth-api-routes.ts:API 白名单规则

约束:

  • 路由规则只在一处定义,避免多处分叉
  • 页面与 API 规则分别维护,但命名和模式保持一致

数据流与行为流

场景 A:未登录访问受保护页面

  1. 用户进入受保护路由
  2. auth.global 判定未登录
  3. 跳转 /login?redirect=<原路由>
  4. 登录成功后读取并校验 redirect
  5. 合法则回跳,不合法则去默认页

场景 B:会话过期后访问受保护 API

  1. 请求命中 API 守卫
  2. 会话失效,返回 401
  3. 前端拦截到 401,清理登录态
  4. 若当前在受保护页面,触发跳转到登录页

场景 C:首页双态展示

  1. 首页属于白名单可访问
  2. 页面内部根据 loggedIn 渲染访客态或用户态
  3. 刷新会话后自动切换显示

测试与验收标准

路由访问测试

  • 未登录访问受保护页应跳登录并保留 redirect
  • 已登录访问登录/注册页应跳转到有效 redirect 或默认页
  • 白名单页面在未登录下可直接访问

API 鉴权测试

  • 未登录访问受保护 API 返回 401
  • 白名单 API 未登录可正常请求
  • 过期会话访问受保护 API 返回 401

安全测试

  • redirect 注入(http://...//...javascript:...)均被拦截
  • 白名单边界:/login-xxx 不应命中 /login
  • 不出现登录页/注册页重定向死循环

体验测试

  • 首页登录态切换稳定,不出现明显错误闪烁
  • 登出后状态同步清空,页面表现与未登录一致

渐进实施顺序

  1. 增加路由规则常量与 redirect 校验工具
  2. 落地前端全局中间件(先页面策略)
  3. 落地 API 守卫中间件(再后端兜底)
  4. 接入统一会话组合式函数,替换分散状态判断
  5. 补齐回归测试与手工验收清单

非目标(本次不做)

  • 角色/权限点(RBAC)细粒度控制
  • 多租户权限隔离
  • OAuth、SSO 等第三方登录
  • 动态远端下发白名单配置

风险与应对

  • 风险:误将必要接口放入受保护导致前端初始化失败
    应对:白名单初始集合最小化后,逐个验证关键页面
  • 风险:新增页面忘记考虑权限属性
    应对:默认拦截策略天然兜底,新增页面仅在确需公开时加入白名单
  • 风险:重定向逻辑复杂导致跳转异常
    应对:redirect 工具函数集中实现,并覆盖异常值测试

设计结论

在当前项目阶段,采用“前端全局路由守卫 + 服务端 API 统一守卫”的登录优先模型,能以最小复杂度建立稳定安全边界,并保留后续扩展(认证辅助页、业务页拆分、角色权限)的空间。