2026-07-17 13:50:30 +08:00
|
|
|
# Xuqm RN 配置文件规范
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
Xuqm RN 扩展 SDK 只支持一种自动初始化方案:宿主保存一份平台签发的 `.xuqmconfig` 原始加密文件,并用 `withXuqmConfig()` 包装 Metro。不得创建 TypeScript 配置副本、手动 alias 或在业务代码硬编码 appKey。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## common-only 项目
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
只使用 `@xuqm/rn-common` 的文件、时间、加密、设备、UI 或自定义网络能力时:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
- 不放 `.xuqmconfig`
|
|
|
|
|
- 不使用 `withXuqmConfig()`
|
2026-07-26 23:47:30 +08:00
|
|
|
- 不存在也不需要调用 `XuqmSDK.initialize()`
|
2026-07-17 13:50:30 +08:00
|
|
|
- 不调用 `XuqmSDK.login()`
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
common-only 模式保持零初始化、零登录。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 扩展 SDK 项目
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
安装 update、bugcollect、xwebview 等扩展后,将平台签发的原始文件放到:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```text
|
2026-07-26 23:47:30 +08:00
|
|
|
src/assets/config/<平台下载的文件名>.xuqmconfig
|
2026-06-15 01:44:20 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-26 23:47:30 +08:00
|
|
|
文件名由租户平台决定,不要求宿主改名。该目录没有 `.xuqmconfig` 时保持
|
|
|
|
|
common-only;恰好一个时自动使用;存在多个时构建明确失败,禁止按名称或排序猜测
|
|
|
|
|
权威配置。不会扫描其它目录。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
Metro 配置只包装一次:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```js
|
2026-07-20 19:31:43 +08:00
|
|
|
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
|
|
|
|
|
const { withXuqmConfig } = require('@xuqm/rn-common/metro')
|
2026-06-18 15:34:48 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
const baseConfig = mergeConfig(getDefaultConfig(__dirname), {
|
|
|
|
|
// 宿主自己的 Metro 配置
|
2026-07-20 19:31:43 +08:00
|
|
|
})
|
2026-06-18 15:34:48 +08:00
|
|
|
|
2026-07-20 19:31:43 +08:00
|
|
|
module.exports = withXuqmConfig(baseConfig)
|
2026-06-18 15:34:48 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 自动初始化时序
|
2026-06-18 15:34:48 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```text
|
|
|
|
|
Metro 读取唯一 .xuqmconfig
|
2026-07-26 23:47:30 +08:00
|
|
|
→ 验证平台 Ed25519 签名
|
|
|
|
|
→ 校验 V2 Schema、有效期和 canonical JSON
|
|
|
|
|
→ AES-GCM 解密
|
2026-07-17 13:50:30 +08:00
|
|
|
→ 生成构建期传输模块
|
|
|
|
|
→ 注册 @xuqm/rn-common/internal 为 pre-main module
|
|
|
|
|
→ 使用 appKey 请求远程 SDK 配置
|
|
|
|
|
→ 初始化唯一 HTTP/扩展上下文
|
|
|
|
|
→ 执行业务入口
|
2026-06-15 01:44:20 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
pre-main 机制保证初始化不受 Metro `inlineRequires` 和业务模块加载顺序影响。解密开始后,所有 `awaitInitialization()` 调用都等待同一个 Promise。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 原生配置同步
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-26 23:47:30 +08:00
|
|
|
Android Release 构建任务会在 V2 验签和 Bundle 构建成功后,把同一份源文件复制到:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```text
|
2026-07-26 23:47:30 +08:00
|
|
|
android/app/build/generated/xuqm/assets/config/config.xuqmconfig
|
2026-06-15 01:44:20 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-26 23:47:30 +08:00
|
|
|
该位置是被 Git 排除的固定构建目标,不是第二配置源。宿主只维护
|
|
|
|
|
`src/assets/config/` 中平台下载的唯一 `.xuqmconfig`;不得把副本写进 Android/iOS
|
|
|
|
|
源码目录。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 加密文件
|
2026-06-18 15:40:19 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
文件格式:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```text
|
2026-07-26 23:47:30 +08:00
|
|
|
XUQM-CONFIG-V2.{keyId}.{salt}.{iv}.{ciphertextAndTag}.{signature}
|
2026-06-15 01:44:20 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-26 23:47:30 +08:00
|
|
|
- Ed25519 验签覆盖前五段的精确 ASCII,必须先验签再解密
|
|
|
|
|
- `keyId` 只能命中 SDK 内置的 X.509 Ed25519 公钥集合
|
|
|
|
|
- PBKDF2-HMAC-SHA256 + AES-256-GCM 仅用于混淆正文
|
2026-07-17 13:50:30 +08:00
|
|
|
- 12 字节 IV
|
|
|
|
|
- 16 字节认证标签
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-26 23:47:30 +08:00
|
|
|
明文必须是键按字典序排列、无空白、忽略 null 的 canonical JSON。必要字段为
|
|
|
|
|
`schemaVersion=2`、`configId`、正整数 `revision`、`issuedAt`、`appKey`、`appName`、
|
|
|
|
|
`serverUrl` 和 `signingKey`;`expiresAt` 省略表示长期有效。平台重新生成配置后,
|
|
|
|
|
旧 `configId/revision` 只会阻止后续 Release 构建,不会追溯影响已经安装的 App。
|
2026-07-20 19:31:43 +08:00
|
|
|
|
|
|
|
|
远程配置响应中的 `apiUrl` 是业务扩展服务地址,与配置文件中的平台地址职责不同:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
配置文件 serverUrl/baseUrl
|
|
|
|
|
→ platformUrl
|
|
|
|
|
→ /api/sdk/config、整包更新、插件 release set 等 Xuqm 平台接口
|
|
|
|
|
|
|
|
|
|
平台返回 apiUrl
|
|
|
|
|
→ 业务扩展服务
|
|
|
|
|
→ 只有明确依赖该服务的扩展能力使用
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
禁止把远程 `apiUrl` 写回公共 HTTP 基础地址,否则会把版本检查错误地发送到 App 自身业务服务。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 登录
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
初始化与业务登录是两个动作。App 登录完成后只同步一次公共会话:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```ts
|
2026-07-20 19:31:43 +08:00
|
|
|
import { XuqmSDK } from '@xuqm/rn-common'
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-20 19:31:43 +08:00
|
|
|
await XuqmSDK.awaitInitialization()
|
2026-07-17 13:50:30 +08:00
|
|
|
await XuqmSDK.login({
|
|
|
|
|
userId: 'user-id',
|
|
|
|
|
accessToken: 'access-token',
|
|
|
|
|
name: '姓名',
|
|
|
|
|
phone: '手机号',
|
2026-07-20 19:31:43 +08:00
|
|
|
})
|
2026-07-17 13:50:30 +08:00
|
|
|
```
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
退出时只调用:
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
```ts
|
2026-07-20 19:31:43 +08:00
|
|
|
await XuqmSDK.logout()
|
2026-06-15 01:44:20 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
扩展包不得各自初始化或登录,业务代码不得导入 `@xuqm/rn-common/internal`。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 失败规则
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
- 扩展项目缺少配置:`awaitInitialization()` 明确报错。
|
2026-07-26 23:47:30 +08:00
|
|
|
- 签名、V2 Schema、canonical JSON 或有效期校验失败:构建立即失败。
|
|
|
|
|
- 远程配置失败且存在七天内的最后成功配置:进入 `degraded` 并继续启动。
|
|
|
|
|
- 首次启动没有缓存:扩展能力返回 `XUQM_NOT_READY`,宿主核心业务继续运行。
|
2026-07-17 13:50:30 +08:00
|
|
|
- 不自动切换到硬编码配置,不静默创建默认租户,不提供多级兼容回退。
|
|
|
|
|
- common-only 项目没有配置时不会执行初始化,也不会报错。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
## 安全约束
|
2026-06-15 01:44:20 +08:00
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
- 不打印密文或解密后的配置。
|
|
|
|
|
- 不提交明文 appKey、signingKey 或租户密钥。
|
|
|
|
|
- 不在多个目录手工维护配置副本。
|
|
|
|
|
- `.xuqmconfig` 变更必须通过平台重新签发。
|
2026-07-26 23:47:30 +08:00
|
|
|
- 最后成功配置不会明文写入 AsyncStorage。SDK 使用签发配置中的 `signingKey`、
|
|
|
|
|
appKey、包名和平台地址派生独立 AES-GCM 缓存密钥,密钥材料不落缓存;篡改、
|
|
|
|
|
跨包或跨环境读取都会失败。该边界属于应用沙箱内加密,不宣称具备 Android
|
|
|
|
|
Keystore/iOS Keychain 的硬件密钥保密性。
|
|
|
|
|
- `signingKey` 是随客户端配置交付、可被终端提取的请求签名材料,只用于请求完整性
|
|
|
|
|
标记和本地缓存派生,不是 License、授权凭据或服务端秘密,服务端不得仅凭该签名
|
|
|
|
|
授予敏感权限。
|
|
|
|
|
- 用户数据、灰度发布、上传构建制品等敏感 API 仍必须使用登录得到的用户
|
|
|
|
|
`accessToken` 或 Jenkins 发布流水线的独立 Bearer,并由服务端执行真实授权。
|