多租户html输出必须禁用静态缓存并按x-tenant-id分片,data-*属性仅限展示层使用,api路径须由网关注入响应头,ejs模板需显式传参隔离作用域,htmlhint规则应按租户分级配置。

租户专属 HTML 输出必须禁用静态缓存
CDN 或反向代理缓存带租户标识的 HTML 页面,是多租户系统最典型的缓存污染事故。一旦 /dashboard 被缓存为租户 A 的版本,租户 B 刷新就看到 A 的菜单、logo 和数据入口。
- 必须在 SSR 响应头中添加
Vary: X-Tenant-ID(推荐)或Vary: Tenant-Slug,让缓存层识别租户维度 - 禁止对含
data-tenant-属性的 HTML 片段启用Cache-Control: public;可设为private, max-age=0, no-cache - 若使用 Edge/CDN 缓存策略,需按请求头
X-Tenant-ID做 key 分片,不能仅靠 URL 路径做缓存键
data-* 属性只允许用于展示层,不可参与路由或 API 构造
把 data-api-base="/api/v1/acme" 写进 ,再让 JS 读取拼接请求地址,等于把租户上下文控制权交到前端——攻击者改 DOM 就能调其他租户接口。
- API 基础路径必须由网关注入响应头(如
X-Api-Base),JS 通过document.querySelector('meta[name="api-base"]')读取(该<meta>由 SSR 安全写入) -
data-tenant-slug只能用于 CSS 主题切换、埋点打标或调试日志,不许出现在fetch()、location.href、router.push()等任何运行时逻辑中 - 模板中禁止出现
href="/t//settings"这类动态拼接——应由后端生成完整 URL 并传入tenantSettingsUrl变量
EJS 模板中租户变量必须作用域隔离
EJS 的 locals 是共享对象,若在 layout.ejs 里直接写 ,后续 partial 里再声明同名变量会覆盖,导致主题类名错乱或权限判断失效。
- 每个 partial 渲染前显式传参:
,不依赖隐式继承 - 避免在模板顶层用
,改用直接消费传入变量 - 租户相关过滤器(如
tenantLogoUrl)必须注册为函数,而非在模板内写三元表达式:,便于统一鉴权和 fallback
HTMLHint 规则需按租户分级启用
租户定制页面常引入第三方组件或内嵌脚本,盲目启用全部 HTMLHint 规则会导致 CI 失败——比如某租户要求保留 <font></font> 标签做邮件模板兼容,但 font-face-require 规则会报错。
- 基础规则(
doctype-html5、tag-pair、id-unique)全局强制启用 - 租户专属规则(如
attr-no-duplication、attr-lowercase)在.htmlhintrc中按目录隔离:"pages/tenant-acme/**": { "attr-no-duplication": false } - 禁止在租户模板中使用
<script></script>内联代码,所有 JS 必须外链并带 integrity 属性;HTMLHint 配置中开启script-src-require和script-strict
租户定制化输出最易被忽略的点:不是属性怎么加,而是谁来读、在哪读、读完之后敢不敢信。data-* 是 DOM 层的便签纸,不是通行证。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











