docs(handoff): 补上 §一·二十五(租户域名)—— 上一版幂等判断被正文引用误判,整节没写进去
上一版脚本用 "§一·二十五" in text 当"已存在就跳过"的判断,而 §一·二十四 的结尾 正好有「见下一节(§一·二十五)」这句话 → 误判为已存在 → 整节没落进文档。 教训:幂等判断要认**标题**(## §一·二十五),不能认"整篇里有没有这个词"; 并且写完要**自查一次**标题能否找到(这次加了)。 另:本次提交还顺带记录了一条同类教训 —— 在 PowerShell 里用 git commit -m "带中文引号的消息" 会被 shell 拆成多个 pathspec(这次就炸了)。本项目一直用消息文件,就是为此。
This commit is contained in:
@@ -1999,3 +1999,64 @@ input.dispatchEvent(new Event('change', { bubbles: true }))
|
|||||||
> 但客户端提了一个**新的架构要求**(租户按域名前缀区分,如 `t1.xxx.com` / `t2.xxx.com`),
|
> 但客户端提了一个**新的架构要求**(租户按域名前缀区分,如 `t1.xxx.com` / `t2.xxx.com`),
|
||||||
> 它会同时影响 PC 登录、H5 微信授权回调、小程序域名白名单与二维码 scene ——
|
> 它会同时影响 PC 登录、H5 微信授权回调、小程序域名白名单与二维码 scene ——
|
||||||
> 见下一节(§一·二十五),那里记录了分析与待拍板的点。
|
> 见下一节(§一·二十五),那里记录了分析与待拍板的点。
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §一·二十五 租户域名:用子域前缀判断"这是哪个单位的站点"
|
||||||
|
|
||||||
|
> 客户原话:**不同的租户采用不同的地址前缀来获取租户ID,如 t1.xxx.com 和 t2.xxx.com**
|
||||||
|
> 落地提交:`d05cec0`(细节与完整论证在那条提交信息里,这里只留要点与待办)
|
||||||
|
|
||||||
|
### 它解决什么
|
||||||
|
|
||||||
|
在此之前"这次是哪个单位"**都要人填或扫**:PC 登录手填租户码(跨单位重名时报 `40012`
|
||||||
|
「请填写租户码」,而用户根本不知道该填什么)、H5 与小程序靠扫码 scene。
|
||||||
|
而域名本身就是"我是谁"最自然的答案 —— 在 `t1.xxx.com` 上打开页面时,不该再问。
|
||||||
|
|
||||||
|
前缀用 **`t{tenantId}`**,与扫码 scene 的 `t{tid}` 是**同一套约定**(scene 早就是 `t1-k17`)。
|
||||||
|
|
||||||
|
### ★ 唯一必须守住的规则:Host 定位,token 授权
|
||||||
|
|
||||||
|
```
|
||||||
|
未登录 → Host 决定"去哪个单位里找这个人" (定位)
|
||||||
|
已登录 → 一律以 token 里的 tenantId 为准 (授权)
|
||||||
|
并且:Host 与 token 的租户**不一致就拒绝**(403)
|
||||||
|
```
|
||||||
|
|
||||||
|
不校验后者,在 `t1.xxx.com` 上拿 t2 的 token 就能读 t2 的数据 ——
|
||||||
|
而**界面一切正常**(前端本来就会带 token),是个安静、极难被发现的越权。
|
||||||
|
|
||||||
|
### 两条来自微信的硬约束(决定架构,不是可选)
|
||||||
|
|
||||||
|
1. **公众号网页授权的「授权回调页面域名」只能配一个** → 每租户一个子域时,
|
||||||
|
回调**统一落在主域名**,再按 scene 里的 `t{tid}` 302 回该单位站点
|
||||||
|
(复用既有的 state 走一圈 + token 放 fragment 的机制)。
|
||||||
|
2. **小程序的 request 合法域名是固定白名单**,配不了 `*.xxx.com` →
|
||||||
|
**小程序端用不了域名**,继续靠 scene 里的 `t{tid}`。这点客户需要知道。
|
||||||
|
|
||||||
|
### 另一个已处理的坑
|
||||||
|
|
||||||
|
开发期 `base` 是 `localhost`,真按它回跳会把手机上的学生从穿透域名扔到
|
||||||
|
`t1.localhost`(打不开,且是浏览器级失败,看不出是我们跳错了)→
|
||||||
|
H5 回跳里加了本机地址判断,本机 base 一律回退到 `h5-base-url`。
|
||||||
|
|
||||||
|
### 语义:`null` 不是错误
|
||||||
|
|
||||||
|
解析不出来 = "这是统一入口/单域名部署",调用方回退老路径。
|
||||||
|
所以**单域名部署仍然完全合法**(`enabled: false`)。
|
||||||
|
|
||||||
|
### E2E:`scripts/e2e-m1-tenant-domain.py` → 16/16
|
||||||
|
|
||||||
|
t1 域名免租户码登录 / 与手填租户码矛盾时明确报错 / **跨租户 token → 403** /
|
||||||
|
四种边界回退(非 t 前缀、租户不存在、主域名、无关域名)。
|
||||||
|
用 `http.client` 伪造 `Host`(urllib 会按 URL 自己算 Host,覆盖不掉)。
|
||||||
|
全量 **676 条断言、0 失败**。
|
||||||
|
|
||||||
|
### ⏳ 需拍板 / 待办
|
||||||
|
|
||||||
|
1. **正式域名(base)** + 泛域名解析 + **泛域名证书 `*.xxx.com`**(备案一个主域名即覆盖子域)。
|
||||||
|
2. **要不要保留"手填租户码"**:我保留了(统一入口、老客户端、运维排查都需要)。
|
||||||
|
3. 小程序端确认继续靠 scene(上面第 2 条硬约束)。
|
||||||
|
4. **补文档**:这条新需求目前只落在代码与本节,应补进 `数据信息填报系统PRD.md`
|
||||||
|
与 `03-接口设计.md`(登录段的"租户从哪来"要多一种来源)。
|
||||||
|
|||||||
Reference in New Issue
Block a user