多租户定制化html输出必须禁用静态缓存并按x-tenant-id做缓存分片,且响应头强制设置vary: x-tenant-id,否则会导致租户越权可见;api路径须由网关注入响应头,data-*属性仅限展示层使用;模板变量需作用域隔离,禁止内联脚本,htmlhint规则须按租户分级配置。

多租户定制化 HTML 输出必须禁用静态缓存,并按 X-Tenant-ID 做缓存分片,否则租户 A 的页面被 CDN 缓存后,租户 B 刷新就看到 A 的菜单、logo 和数据入口——这不是样式错乱,是越权可见。
为什么 Vary: X-Tenant-ID 是硬性要求
CDN 或反向代理缓存默认只认 URL,/dashboard 对所有租户都一样。不加 Vary: X-Tenant-ID,缓存层根本不知道该为不同租户存多份副本。
- 必须在 SSR 响应头中显式设置:
Vary: X-Tenant-ID(推荐)或Vary: Tenant-Slug - 若用 Edge/CDN,需配置缓存 key 包含
X-Tenant-ID值,不能只靠路径 -
Cache-Control: public与data-tenant-属性共存会直接导致缓存污染,此时应设为private, max-age=0, no-cache
data-* 属性只能用于展示层,不能参与任何运行时逻辑
把 data-api-base="/api/v1/acme" 写进 ,再让 JS 拼接请求地址,等于把租户上下文控制权交到前端——用户改 DOM 就能调其他租户接口。
- API 基础路径必须由网关注入响应头(如
X-Api-Base),JS 通过document.querySelector('meta[name="api-base"]')安全读取 -
data-tenant-slug只允许用于 CSS 主题切换、埋点打标或调试日志,禁止出现在fetch()、location.href、router.push()中 - SSR 渲染时可输出
data-theme="acme",但 JS 不据此构造 URL 或切换功能开关
EJS 模板中租户变量必须作用域隔离
locals 是共享对象, 在 layout.ejs 里声明后,后续 partial 若再定义同名变量会覆盖,导致权限判断失效或主题类名错乱。
- 模板内禁止直接赋值同名变量,应始终消费传入参数,例如
- 租户相关过滤器(如
tenantLogoUrl)必须注册为函数,而非模板内写三元表达式: - href、src 等属性禁止拼接:
href="/t//settings"→ 应由后端生成完整 URL 并传入变量
HTMLHint 规则必须按租户分级配置
某租户要求保留 <font></font> 标签做邮件模板兼容,但全局启用 font-face-require 规则会导致 CI 失败;盲目启用全部规则只会卡住交付节奏。
- 基础规则(
doctype-html5、tag-pair、id-unique)全局强制启用 - 租户专属规则在
.htmlhintrc中按目录隔离:"pages/tenant-acme/**": { "attr-no-duplication": false } - 禁止在租户模板中使用内联
<script></script>,所有 JS 必须外链并带integrity属性
真正难的不是写对一行 data-tenant-id,而是确保它从网关头进入、在 SSR 中注入、被浏览器忽略、被 JS 忽略、被缓存层识别、被 HTMLHint 放行——每个环节断掉,都会变成线上事故。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











