深色模式
部署与安全
上线前要定三件事:谁能嵌、谁能看、数据到哪为止。
允许哪些宿主嵌
报表服务的配置文件 conch.config.json:
json
{
"embed": {
"allowedOrigins": ["https://hdms.example.com", "https://portal.example.com"]
}
}配了这一段,报表服务在每个响应上发 Content-Security-Policy: frame-ancestors https://hdms.example.com https://portal.example.com——只有这两个站点能把报表嵌进 iframe,别人嵌会被浏览器挡掉。
不配这一段就不发这个头,任何站点都能嵌。本地开发和演示可以不配,正式部署应当配上。改完重启报表服务生效。
也要检查反向代理
有些网关默认往回加 X-Frame-Options: SAMEORIGIN,那一条会让所有跨域嵌入失败,而且比 frame-ancestors 更粗暴。报表服务自己不发这个头,发了就是代理加的,去代理上关掉。
谁能看到报表
这一版的查看页没有自己的登录页。 访问控制靠部署位置来做,两种可行方案:
一、放在内网 / 只开给宿主的网络 报表服务不暴露到公网,只有能访问宿主系统的人能访问它。最省事,适合院内、厂内部署。
二、由宿主反向代理,鉴权在代理上做 把报表服务挂到宿主自己的域名下,用户的登录态(cookie)天然带上,也没有跨站 cookie 的麻烦:
nginx
location /report/ {
# 这里接你们自己的鉴权(auth_request / JWT 校验 / 基础认证)
auth_request /your-auth;
proxy_pass http://127.0.0.1:5088/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# 多租户部署时由代理钉死租户,浏览器改不了
proxy_set_header X-Conch-Tenant hospital-a;
# 对话式生成走 SSE,必须关缓冲
proxy_buffering off;
}宿主页面里 iframe 就写 src="/report/?id=...&view=1",同源,frame-ancestors 也不用配了。
别把报表服务直接开到公网又不设任何认证
查看态链接里只有报表 id,能猜到 id 的人就能看到那张报表的数据。公网部署一定要在前面加一道认证。
租户目前从请求头 X-Conch-Tenant 取,浏览器发不出这个头,所以多租户必须在反向代理上注入,不能指望前端传。
按登录身份把宿主的科室、区域、角色带进报表(JWT 换票)在路线上,还没上线;现在需要这类隔离的场合,按租户分开部署。
数据边界在哪
| 这一层 | 做了什么 |
|---|---|
| 数据库账号 | 只读账号,写操作在库上就被拒 |
| SQL 守卫 | 报表里的每条 SQL 执行前都过一遍守卫,只放行单条只读查询 |
| 查看态下发内容 | 规格里的 SQL 正文不下发到浏览器,查看页拿到的只有渲染要用的结构 |
| 客户端传参 | 查看态只报版本号,规格由服务端按那一版取,浏览器传来的规格不作数 |
| 行级范围 | 由服务端按调用者身份注入系统参数,SQL 里引用它做过滤 |
| 模型 | 只在设计态参与,只看表结构与字典值;运行态没有模型,数据从库到浏览器不经过任何模型 |
lock= 只是界面口径(控件只读),不是权限。会改链接的人能把它去掉。要做到「这个人只能看到自己那部分数据」,必须落在行级范围参数上,不能靠 lock。
HTTPS
宿主是 https://、报表服务是 http://,浏览器会按混合内容直接拦掉 iframe,页面上一片空白。两边都上 HTTPS,或者都走同一个域名下的反向代理。
链接里别放这些
- 不要放 token、密码、签名。查看态链接会进浏览器历史、进 Referer、被人抄给同事。
- 不要放 SQL。报表服务不接受从链接或请求体传 SQL 来执行。
- 不要放能反推出别人身份的值——链接是可以改的,把
dept=A改成dept=B的成本是零。
版本怎么发才稳
- 嵌出去的链接默认跟着当前发布版走。设计者改草稿不会影响生产页面,重新发布才会更新。
- 要在改版期间保持页面不变,把链接钉在某一版:
&version=v3。 - 要给客户一份不会再变的数,发布时选「数据快照」,那一版打开不查库。
version=draft只用于发布前自查,别出现在给客户的链接里。
