Appearance
平台适配与生产配置
本章对应 CLI 生成的 host/vue 项目。运行库升级不会自动改写已生成项目;旧项目应对照以下文件迁移,保留自己的后台协议和业务授权规则。
平台模块职责与兼容入口
host 的 src/platform.ts 保留原有公开函数的再导出,现有 import { login, canOpenRoute } from './platform' 无需修改。实现按职责拆分:
| 文件 | 职责 | 业务接入位置 |
|---|---|---|
src/platform/auth.ts | 登录、恢复、注销,以及身份与菜单的完整加载 | 固定接口路径、后台响应解包、认证协议 |
src/platform/session.ts | 私有内存 Token、版本、候选凭据和公开身份白名单 | 凭据提交与清理;不要把 Token 放进公开身份 |
src/platform/menu.ts | 身份与菜单解码、本地可信应用注册表、详情归属 | 响应约束、菜单协议与业务归属规则 |
src/platform/access.ts | canOpenRoute 导航准入 | 功能权限、route/tabId 与资源标识规则 |
src/configs/platformSecurity.ts | 平台写接口 CSRF 配置 | 服务端签发方式、请求头、是否必需 |
认证控制器仍使用 generation 判断异步结果是否属于当前登录操作。身份与全部菜单加载成功后,Token 只暂存为候选;控制器确认有效后才激活。退出先撤销本地身份,再使用局部旧 Token 请求注销,失败不恢复旧身份。publicSession() 只复制 user.id/name 和权限,后台多余字段不会进入子应用公开会话。
身份与菜单数量限制
模板在遍历数组前检查数量;不截断授权数据,超限、重复或协议错误均拒绝本次会话初始化,不进入部分就绪工作区。
| 响应字段 | 限制 |
|---|---|
session/login 的 permissions | 最多 500 项;每项非空、最多 120 字符;不允许重复 |
session/login 的 appIds | 不超过本地可信注册表数量;不允许重复或未知 appId |
menus 的 apps | 数量必须与当前身份的 appIds 一致;必须完整覆盖,不得遗漏或重复 |
每应用 menus | 最多 500 个节点,含分组;最多 8 层 |
菜单仍检查应用内唯一 menuId、宿主内唯一 tabId、路由、文本长度与图标标识。后台不能覆盖本地 entry/shareToken。这些是浏览器收到并解析 JSON 后的协议约束;网关和后端仍需限制响应体大小。业务需要更高上限时,应同时调整模板约束、测试与服务端分页协议。
Vue 独立认证的 src/apis/auth.ts 同样在遍历前限制最多 500 项权限,拒绝重复、空值、控制字符和超过 120 字符的权限;登录、Cookie 恢复和手动 Token 验证共用此解码,均不截断响应。独立认证没有平台 appIds/menus 协议。
详情准入优先于普通菜单
src/platform/access.ts 先识别详情路径并检查详情策略,再匹配普通菜单。后台即使直接返回详情菜单,也不能跳过功能权限。
默认演示详情必须同时满足:
- 当前身份包含
demo:read。 - 当前应用有
menuId: 'records'且路由为/list的叶菜单。 - route 严格符合
/detail?id=<数字>,ID 为 1–10 位正整数。 - tabId 严格为
<appId>:detail:<同一个ID>。
编码别名、大小写变体、尾斜杠、额外参数和无效 ID 不会通过普通菜单旁路放行。普通菜单继续同时精确匹配 route 和 tabId。本规则仅是模板演示;替换真实详情页面时,应同步修改 access.ts、menu.ts 的所属菜单规则,以及子应用路由权限。业务接口仍须独立校验用户、操作权限、租户与资源归属。
平台 CSRF 接入
host 的 POST /api/login、POST /api/logout 在发送前调用 platformCsrf.getToken({ path, signal })。默认配置位于 src/configs/platformSecurity.ts:
ts
export const platformCsrf: PlatformCsrfConfig = {
required: !import.meta.env.DEV,
headerName: 'X-CSRF-Token',
getToken: () =>
document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content ?? null,
}生产默认要求 CSRF Token,缺失时登录、注销请求不会发送。 默认示例要求服务器在返回入口 HTML 时动态注入 csrf-token meta,其值由服务端签发并与会话协议绑定。不能把真实值写死在 index.html、源码、VITE_* 或静态构建产物中;静态托管通常应替换为初始化接口。开发演示不请求后台;真实认证联调建议显式设为 required: true,验证与生产相同的协议。
例如后端提供同源的 GET /api/csrf,返回 { csrfToken: string },并负责关联会话 Cookie,可将配置改为:
ts
// src/configs/platformSecurity.ts
export const platformCsrf: PlatformCsrfConfig = {
required: true,
headerName: 'X-CSRF-Token',
async getToken({ signal }) {
const response = await fetch('/api/csrf', {
signal,
credentials: 'same-origin',
cache: 'no-store',
})
if (!response.ok) throw new Error('CSRF 初始化失败')
const data: unknown = await response.json()
if (typeof data !== 'object' || data === null ||
!('csrfToken' in data) || typeof data.csrfToken !== 'string') {
throw new Error('CSRF 响应格式无效')
}
return data.csrfToken
},
}接口地址、响应字段和 Cookie 策略按后端协议替换。读取专用 Cookie 的双提交方案也应在此实现,并由后端绑定与验证;HttpOnly 会话 Cookie 无法由 JavaScript 读取,访问 Token 也不能冒充 CSRF Token。
接入点支持同步或异步取值,每次写请求重新读取。异步 provider 应把 signal 传给内部请求;取消或 provider 失败不会发送后续平台写请求。请求头仅允许独立的 X-* 名称;返回 null 表示缺失,空串、空白控制字符、超过 4096 字符等无效值均拒绝。GET session/menus 不读取或携带 CSRF 头,局部请求头不污染 Axios 默认配置。
只有服务端已经提供等效防护,或确认认证完全不依赖浏览器自动携带的 Cookie 时,才可评估显式设置 required: false。VITE_API_CREDENTIALS=false 只关闭跨源 Cookie 携带,同源请求仍可自动携带 Cookie,不能据此关闭 CSRF。
该接线覆盖 host 平台的 login/logout。Vue 独立认证的 src/apis/auth.ts 和其他业务写接口,需要接入同一服务端防护协议;配置平台 hook 不会自动为所有业务请求添加 CSRF。
服务端与部署配合
- 登录、注销和其他有副作用接口都由服务端校验 CSRF,并按协议轮换、失效;GET 不承载写操作。登录接口也需防护登录 CSRF。
- 会话 Cookie 配置
HttpOnly、生产Secure与合适的SameSite;后端验证可信 Origin,必要时按策略检查 Referer。 - 跨源 Cookie 场景同时配置客户端 credentials、精确允许的 Origin 和请求头;携带凭据的 CORS 不使用
*,拒绝未登记来源。仅配置 CORS 无法替代 CSRF 校验。 - 提供 CSRF 值的 HTML/接口禁止共享缓存用户相关值;生产通过 HTTPS 部署。静态站点也必须有真实的服务端签发与验证协议。
Token 能力默认关闭
src/configs/microApps.ts 的业务和报表示例均为 shareToken: false。仅在应用完全可信且确实需要宿主认证时显式开启:
ts
{ appId: 'business', name: '业务应用', entry: 'https://business.example.com/', shareToken: true }它控制 getToken/beginAuthRequest/reportAuthFailure 能力,不等于应用、菜单或业务 API 授权。能力范围始终是当前会话应用与本地白名单的交集,后台不能扩大它。子应用 auto 模式在三项均未授权时使用自己的认证协议;若希望共享宿主认证,应同时完成该白名单与后端接入。host 模式在未完整授权时明确失败。同窗口桥不隔离恶意应用代码,开启前必须确认应用信任关系。
开发服务器 CORS
host/vue 公共 Vite 配置仅允许 http(s)://localhost、127.0.0.1、[::1] 的可选端口来源跨源加载开发资源,以支持本机 Wujie 联调;外部域名、伪装后缀和 Origin: null 均不在白名单中。
远程联调时,在对应生成项目的 vite.config.ts 显式列出所需来源,例如:
ts
server: {
port: 7101,
strictPort: true,
cors: { origin: ['https://host.dev.example.com'] },
}需要本机与远程同时使用时逐一补充来源,不恢复 cors: true。Vite 的开发 CORS 不配置生产静态资源服务器,也不配置 API 网关;生产资源、API 与 CSP 需要分别按部署环境设置。
模板清理与桌面范围
base 只保留公共且实际使用的文件。旧 router/index.ts 总被 overlay 覆盖,已移除;未参与业务的 router/demo.ts、stores/demo.ts、计数器 views/demo/index.vue 一并移除。Vue 默认 /demo 路由删除,保留 /list 业务列表和 /detail 详情示例,以及独立登录、主页和拒绝页。
LanguageSelect.vue 移到 host overlay;Vue 使用自己的外壳语言切换。requestUtils.ts 移到 vue overlay;host 继续使用独立的 utils/request.ts。语言映射删除计数示例的未使用词条,每轮挂载仍创建独立 Pinia 与语言实例。
standard 公共布局和 custom 模板统一面向桌面,移除移动宽度媒体查询和 dvh,使用 100vh 与工作区内部滚动。保留 prefers-reduced-motion 无障碍设置。已有业务自行增加的路由与页面无需删除;迁移时仅清理确认未使用的演示文件与导入。