目 录CONTENT

文章目录

文件服务鉴权前端接入改造记录

七月流火
2026-08-12 / 0 评论 / 0 点赞 / 5 阅读 / 0 字

文件服务鉴权前端接入改造记录

配套后端文档:见同目录 nginx auth_request 文件鉴权实施文档 本文记录前端侧如何让文件 URL 在鉴权网关下正常展示与下载

一、背景

文件服务域名(测试 filetest.xxx.cn / 生产 files.xxx.cn)已启用 nginx auth_request 鉴权: 浏览器直接访问文件 URL 不再放行,必须携带登录令牌 file_token 才能通过校验(401/403 拒绝)。 而 file_token 是 JWT(base64url 字符),放在 URL query 里不会破坏编码,因此前端统一采用 「URL 追加 query 参数」的方式给文件地址附加凭证。

与此同时,多环境/多项目共存于同一文件服务域名,nginx 需要按 ?tenant= 参数分流到对应环境的 鉴权服务(测试线/生产线各自校验各自的登录态)。因此前端在拼 file_token 的同时还必须带上 tenant(项目+环境标识,如 xxx-dev / xxx-prod)。

一句话总结:文件 URL = 原地址 + ?file_token=&tenant=<项目-环境>。

二、核心机制:withFileToken / stripFileToken / injectFileToken

各前端工程在 src/utils/auth.js(或 utils/auth.js)中实现三个工具函数:

1. withFileToken(url, token) —— 主动拼接

手动场景(上传返回、模板下载、新页面跳转)给文件 URL 拼 file_token + tenant。

关键点:

  • 幂等:URL 已带 file_token 则不重复拼,已带 tenant 则不重复拼
  • 先拼 tenant 再拼 token,分隔符按 URL 是否已有 ? 自动选 ? 或 &
  • tenant 来自构建环境变量 process.env.VUE_APP_FILE_TENANT,未配置时不拼(兼容旧行为)
const FILE_TOKEN_QUERY = 'file_token'
const FILE_TENANT_QUERY = 'tenant'
const FILE_TENANT = process.env.VUE_APP_FILE_TENANT || ''

export function withFileToken(url, token) {
  if (!url || typeof url !== 'string') return url
  if (!token) return url
  const hasToken = url.indexOf(FILE_TOKEN_QUERY + '=') >= 0
  const hasTenant = !FILE_TENANT || url.indexOf(FILE_TENANT_QUERY + '=') >= 0
  if (hasToken && hasTenant) return url
  let out = url
  if (!hasTenant) {
    const sep = out.indexOf('?') >= 0 ? '&' : '?'
    out = out + sep + FILE_TENANT_QUERY + '=' + encodeURIComponent(FILE_TENANT)
  }
  if (!hasToken) {
    const sep = out.indexOf('?') >= 0 ? '&' : '?'
    out = out + sep + FILE_TOKEN_QUERY + '=' + encodeURIComponent(token)
  }
  return out
}

2. stripFileToken(url) —— 入库前清理

上传/保存到数据库前,把运行时注入的 file_token 和 tenant 都移除,保证库里存的是干净路径。 两个参数都是运行时注入的(token 会过期、tenant 随构建环境变),一律不落库。

export function stripFileToken(url) {
  if (!url || typeof url !== 'string') return url
  const qIdx = url.indexOf('?')
  if (qIdx === -1) return url
  const base = url.slice(0, qIdx)
  const query = url.slice(qIdx + 1)
  const parts = query.split('&').filter(p => p &&
    p.indexOf(FILE_TOKEN_QUERY + '=') !== 0 &&
    p.indexOf(FILE_TENANT_QUERY + '=') !== 0)
  return parts.length ? base + '?' + parts.join('&') : base
}

3. injectFileToken(data) —— 响应数据自动注入

在请求封装层(request.js / interface.js)的响应拦截器里调用:遍历接口返回的数据, 凡是命中文件域名白名单或相对路径的字符串,自动补 file_token + tenant。 这样列表/详情里后端返回的原始文件地址,前端展示时自动带凭证,无需每处手写。

// request.js 响应拦截器片段
response => {
  // ...原有处理
  return injectFileToken(response.data)
}

文件域名白名单(命中才注入,业务接口域名不动):

const FILE_DOMAINS = [
  'file.xxx.cn',
  'files.xxx.cn',
  'filetest.xxx.cn', // 测试域名,务必加!否则测试环境回显不注入凭证
  'file.xxxxxxx.cn'
]

三、tenant 配置:与环境强相关

tenant 值 = 项目标识 + 环境标识,例如本项目 saas 项目:xxx-dev(测试)/ xxx-prod(生产)。

Vue2 工程(9 个)—— 构建环境变量注入

在 .env.development / .env.staging / .env.production 各配一份:

# .env.development
VUE_APP_FILE_TENANT = 'xxx-dev'

# .env.staging
VUE_APP_FILE_TENANT = 'xxx-stage'

# .env.production
VUE_APP_FILE_TENANT = 'xxx-prod'

代码里 process.env.VUE_APP_FILE_TENANT 读取。改环境只动 .env,不动代码,打包自动区分。

uni-app 工程(yiyang-server-ui 等)—— 常量 + 注释

uni-app 无 .env 机制,在 config/api.js 或 config/interface.js 顶部定义常量,与 API_BASE 同步切换:

// 项目+环境租户标识,Nginx 按此分流文件鉴权,与 config/api.js 的 API_BASE 同步切换
const FILE_TENANT = 'xxx-dev'

四、踩过的坑

坑 1:测试域名不在白名单 → 上传回显 401(本次直接根因)

上传成功返回的图片地址是 https://filetest.xxxx.cn/...,但 FILE_DOMAINS 只有生产域名, withFileToken/injectFileToken 判定「非文件域名」→ 不注入 token → 图片 401 无法展示。 修复:白名单补上 filetest.xxx.cn。教训:测试/生产域名都要进白名单。

坑 2:token 是 JWT,必须 encodeURIComponent

file_token 虽是 base64url 字符集(不编码也不会坏),但统一 encodeURIComponent 更稳妥, 防止未来 token 格式变化或拼接其他参数时出错。

坑 3:手工直开点漏网

响应注入只能覆盖「接口返回的数据」,代码里手写的 window.open / window.location.href / img src 直连(模板下载、附件跳转、静态图片)不会自动带凭证。 必须逐个包 withFileToken(url, getToken())。本次共修复:

  • 4 个同源工程 x 6 类模板下载 = 24 处
  • 合同/服务项目附件动态跳转
  • 档案/床位页面 img 直连

坑 4:VUE_APP_* 是构建期变量

改 .env 必须重新 npm run build / npm run dev 才生效,热更新不重载 .env。

五、部署注意事项

  1. nginx map 与 tenant 对齐:测试线 map 里 tenant=xxx-dev / xxx-stage 指向测试 auth, 生产 map 里 tenant=xxx-prod 指向生产 auth,default 建议指向测试 auth 兜底。
  2. 前端重新构建部署:.env 改动是构建期注入,必须重新打包。
  3. 其余 39 个未接入工程(大屏/uni-app 等)待验证后按同一模板铺开。

六、验证方法

  1. 登录后上传图片,回显 URL 应形如:https://filetest.xxxx.cn/2026/08/12/xxx.jpg?file_token=xxx&tenant=xxx-dev
  2. 无 token 直开该 URL -> 401;带完整参数 -> 200 出图
  3. 列表页图片、详情页附件、模板下载、新页面跳转均能正常打开
0

评论区