@microi.net/cli 5.5.9 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codebuddy-plugin/marketplace.json +2 -2
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/README.md +71 -71
- package/assets/build-meta.json +5 -5
- package/assets/feature-matrix.json +129 -129
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/codex-marketplace.json +20 -20
- package/scripts/mcp-codex-stdio-adapter.js +189 -189
- package/scripts/mcp-trae-windows-launcher.cmd +21 -21
- package/scripts/microi-cli-mcp.js +7 -7
- package/scripts/microi-codex-broker.js +447 -447
- package/scripts/microi-codex-router.js +582 -582
- package/scripts/microi-skills.meta.json +336 -336
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +3566 -3566
- package/skills/README.md +3 -3
- package/skills/ai-engine/SKILL.md +45 -45
- package/skills/ai-platform-governance/SKILL.md +177 -177
- package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -190
- package/skills/app-store/SKILL.md +177 -177
- package/skills/business-blueprint/SKILL.md +85 -85
- package/skills/datasource-engine/SKILL.md +91 -91
- package/skills/dos-orm/SKILL.md +81 -81
- package/skills/dos-orm/references/api-reference.md +229 -229
- package/skills/email-engine/SKILL.md +80 -80
- package/skills/email-engine/references/v8-email.md +34 -34
- package/skills/job-engine/SKILL.md +154 -154
- package/skills/message-notification/SKILL.md +126 -126
- package/skills/message-notification/agents/openai.yaml +5 -5
- package/skills/message-notification/references/contracts.md +102 -102
- package/skills/microi-ai-app-auth.js +652 -652
- package/skills/microi-ai-application/SKILL.md +102 -102
- package/skills/microi-ai-application/agents/openai.yaml +4 -4
- package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
- package/skills/microi-client-frontend/SKILL.md +43 -43
- package/skills/microi-client-frontend/references/progressive-01-3-/345/212/250/346/200/201/346/214/211/351/222/256/347/263/273/347/273/237.md +2 -2
- package/skills/microi-client-frontend/references/progressive-02-8-/350/277/220/350/241/214/346/227/266/351/253/230/351/242/221/345/235/221/345/244/215/347/233/230.md +28 -28
- package/skills/microi-client-frontend/references/progressive-03-vue3-/345/211/215/347/253/257/345/276/256/346/234/215/345/212/241/345/256/277/344/270/273/350/247/204/345/210/231.md +56 -56
- package/skills/microi-codex/SKILL.md +46 -46
- package/skills/microi-codex-installer/SKILL.md +216 -216
- package/skills/microi-codex-installer/agents/openai.yaml +7 -7
- package/skills/microi-datasource-mapping/SKILL.md +67 -67
- package/skills/microi-db-schema/SKILL.md +6 -6
- package/skills/microi-db-schema/references/core-tables.md +18 -18
- package/skills/microi-db-schema/references/form-component-options.md +256 -256
- package/skills/microi-db-schema/references/schema-overview.md +11 -11
- package/skills/microi-db-schema/references/schema.md +46 -46
- package/skills/microi-db-schema/references/table-catalog.md +57 -57
- package/skills/microi-deployment/SKILL.md +18 -1
- package/skills/microi-deployment/references/deployment-matrix.md +101 -101
- package/skills/microi-docs-coverage/SKILL.md +123 -123
- package/skills/microi-docs-coverage/references/capability-map.md +1 -0
- package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
- package/skills/microi-form-engine/SKILL.md +50 -50
- package/skills/microi-form-engine/references/component-catalog.md +218 -218
- package/skills/microi-form-engine/references/data-source-events.md +124 -124
- package/skills/microi-form-layout/SKILL.md +13 -13
- package/skills/microi-form-layout/references/progressive-01-3-/344/270/211/347/247/215/345/210/206/347/273/204/347/232/204/345/255/230/345/202/250/344/270/216/351/205/215/347/275/256.md +235 -235
- package/skills/microi-frontend-sdk/SKILL.md +4 -4
- package/skills/microi-frontend-sdk/references/progressive-01-token-/345/275/223/345/211/215/347/231/273/345/275/225/347/224/250/346/210/267/344/270/216/345/275/223/345/211/215/347/273/210/347/253/257/347/231/273/345/275/225/345/215/217/350/256/256.md +175 -175
- package/skills/microi-left-right-layout/SKILL.md +141 -141
- package/skills/microi-microservice/SKILL.md +15 -15
- package/skills/microi-microservice/references/runtime-delivery.md +278 -278
- package/skills/microi-mobile-app-quality/references/progressive-01-4-/351/207/215/350/246/201/346/214/211/351/222/256/345/277/205/351/241/273/345/270/246/345/233/276/346/240/207.md +213 -213
- package/skills/microi-solution-quotation/SKILL.md +78 -78
- package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
- package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
- package/skills/microi-sso/SKILL.md +85 -85
- package/skills/microi-sso/references/acceptance.md +49 -49
- package/skills/microi-sso/references/configuration-and-security.md +53 -53
- package/skills/microi-sso/references/inbound.md +53 -53
- package/skills/microi-sso/references/outbound.md +39 -39
- package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +20 -20
- package/skills/microi-ui/SKILL.md +7 -2
- package/skills/microi-uniapp-frontend/SKILL.md +134 -134
- package/skills/microi-uniapp-frontend/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/210/206/347/261/273-/345/217/214/346/240/217/345/210/227/350/241/250/347/213/254/347/253/213/346/273/232/345/212/250.md +136 -136
- package/skills/microi-uniapp-frontend/references/progressive-02-/345/205/263/351/224/256/344/270/232/345/212/241/350/265/204/344/272/247/344/270/215/345/276/227/351/273/230/350/256/244/351/200/211/344/270/255.md +124 -124
- package/skills/module-engine/SKILL.md +16 -16
- package/skills/ocr-engine/SKILL.md +2 -2
- package/skills/ocr-engine/agents/openai.yaml +1 -1
- package/skills/page-engine/SKILL.md +28 -28
- package/skills/page-engine/references/progressive-01-/346/211/200/346/234/211/347/273/204/344/273/266/347/261/273/345/236/213.md +126 -126
- package/skills/page-engine/references/progressive-02-/347/211/210/346/234/254/345/216/206/345/217/262-/345/271/266/345/217/221/344/277/235/345/255/230/344/270/216/345/233/236/346/273/232.md +42 -42
- package/skills/performance-testing/SKILL.md +120 -120
- package/skills/playwright-e2e/SKILL.md +84 -84
- package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +68 -68
- package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +64 -64
- package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +20 -20
- package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +69 -69
- package/skills/print-engine/SKILL.md +36 -36
- package/skills/production-readonly-audit/SKILL.md +41 -41
- package/skills/report-engine/SKILL.md +71 -71
- package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
- package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
- package/skills/scripts/validate-progressive-disclosure.mjs +52 -52
- package/skills/search-engine/SKILL.md +75 -75
- package/skills/spider-engine/SKILL.md +190 -190
- package/skills/system-observability/SKILL.md +162 -162
- package/skills/system-observability/references/memory-incident-triage.md +73 -73
- package/skills/translate-engine/SKILL.md +1 -1
- package/skills/ui-design/SKILL.md +3 -1
- package/skills/uniapp-mall-assets/SKILL.md +170 -170
- package/skills/unity-integration/SKILL.md +155 -155
- package/skills/unity-integration/agents/openai.yaml +4 -4
- package/skills/unity-integration/references/ai-app-delivery.md +103 -103
- package/skills/unity-integration/references/sdk-api.md +82 -82
- package/skills/unity-integration/references/toolbox-migration.md +66 -66
- package/skills/unity-integration/references/webgl-hosting.md +57 -57
- package/skills/v8-api-config/SKILL.md +378 -378
- package/skills/v8-cache-pattern/SKILL.md +306 -306
- package/skills/v8-crud-api/SKILL.md +29 -29
- package/skills/v8-crud-api/references/progressive-01-/346/237/245/350/257/242/345/210/227/350/241/250-/345/210/206/351/241/265.md +56 -56
- package/skills/v8-debugging/SKILL.md +143 -143
- package/skills/v8-explorer-tree/SKILL.md +64 -64
- package/skills/v8-export-import/references/progressive-01-excellayout-/351/253/230/347/272/247/350/207/252/347/224/261/345/270/203/345/261/200.md +201 -201
- package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +118 -118
- package/skills/v8-export-import/references/progressive-03-/345/256/211/345/205/250-/346/200/247/350/203/275/346/263/250/346/204/217.md +32 -32
- package/skills/v8-file-upload/SKILL.md +154 -154
- package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +133 -133
- package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +138 -138
- package/skills/v8-formengine-http/SKILL.md +107 -107
- package/skills/v8-frontend-events/SKILL.md +22 -22
- package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
- package/skills/v8-frontend-events/references/bluetooth-print.md +247 -247
- package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +50 -50
- package/skills/v8-http-integration/SKILL.md +106 -106
- package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +31 -31
- package/skills/v8-http-integration/references/progressive-02-/351/224/231/350/257/257/345/244/204/347/220/206/346/250/241/345/274/217.md +5 -5
- package/skills/v8-image-processing/SKILL.md +190 -190
- package/skills/v8-image-processing/agents/openai.yaml +4 -4
- package/skills/v8-image-processing/references/api-reference.md +623 -623
- package/skills/v8-menu-buttons/SKILL.md +103 -103
- package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +145 -145
- package/skills/v8-menu-buttons/references/progressive-02-8-/346/250/241/345/274/217-f-/345/220/216/345/217/260/344/273/273/345/212/241/346/214/211/351/222/256-/351/225/277/344/273/273/345/212/241.md +228 -228
- package/skills/v8-menu-buttons/references/progressive-03-10-/345/217/215/346/250/241/345/274/217-/351/201/277/345/205/215.md +43 -43
- package/skills/v8-mongodb/SKILL.md +65 -65
- package/skills/v8-mq-mqtt/SKILL.md +175 -175
- package/skills/v8-mq-mqtt/references/mqtt-production.md +342 -342
- package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +137 -137
- package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
- package/skills/v8-saas-multi-tenant/SKILL.md +223 -223
- package/skills/v8-security/SKILL.md +25 -25
- package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +122 -122
- package/skills/v8-sql-query/SKILL.md +127 -127
- package/skills/v8-table-event/SKILL.md +20 -20
- package/skills/v8-table-event/references/progressive-01-informv8-js-/350/241/250/345/215/225/346/211/223/345/274/200/344/272/213/344/273/266.md +18 -18
- package/skills/v8-table-event/references/progressive-02-/345/211/215/347/253/257/344/272/213/344/273/266/345/220/215-v8-eventname-/345/217/257/350/203/275/347/232/204/345/200/274.md +15 -15
- package/skills/v8-tcp-integration/SKILL.md +1 -1
- package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
- package/skills/v8-template-engine/SKILL.md +59 -59
- package/skills/v8-utilities/SKILL.md +92 -92
- package/skills/v8-utilities/references/client-api-index.md +143 -143
- package/skills/v8-utilities/references/platform-http-routes.md +83 -83
- package/skills/v8-utilities/references/server-api-index.md +74 -74
- package/skills/v8-workflow/SKILL.md +44 -44
- package/skills/vision-engine/SKILL.md +19 -19
- package/skills/vision-engine/references/architecture-and-acceptance.md +34 -34
- package/skills/workspace-conventions/SKILL.md +236 -236
- package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +208 -208
- package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +179 -179
- package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +21 -21
|
@@ -3,61 +3,61 @@
|
|
|
3
3
|
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
4
|
|
|
5
5
|
<!-- microi-progressive:chunk id=v8-security-005 sha256=c2bc7b1ec3ad6240b05537e1ffada3ef2e07e7a10e15102d9e3c8f777ae7ee31 -->
|
|
6
|
-
## 2. 权限校验
|
|
7
|
-
|
|
8
|
-
### DiyToken 是平台会话与权限入口,不替换为 ASP.NET Identity
|
|
9
|
-
|
|
10
|
-
DiyToken 与 `sys_user`、`OsClient`、终端 `did`、共享 Redis 登录态、角色/部门、菜单动作、表权限、数据范围和接口引擎共同构成吾码认证授权体系。它不是“只有一个 Token 字符串”的简化登录:
|
|
11
|
-
|
|
12
|
-
- 登录、SSO、OAuth、Passkey、人脸或访问密钥验证成功后,统一签发/兑换 DiyToken。
|
|
13
|
-
- Token 认证只证明用户和租户;服务端仍按资源、动作和数据范围授权,前端按钮不可代替。
|
|
14
|
-
- 用户停用、角色变化、单终端/全部终端吊销和 Token 轮换都依赖共享会话事实,不依赖单机内存。
|
|
15
|
-
- ASP.NET Identity 可以作为外部身份源的适配参考,但不能整体替换吾码的租户、低代码权限、V8 和在线终端协议。
|
|
16
|
-
|
|
17
|
-
开发新登录入口时只实现“验证身份 -> 获取仍启用的 `sys_user` -> 签发 DiyToken”,禁止并行创建第二套用户、角色、权限 Token 或会话有效期规则。
|
|
18
|
-
|
|
19
|
-
### 平台 FormEngine 授权边界
|
|
20
|
-
|
|
21
|
-
**Token 只完成身份认证,不是任意表的访问凭证。** 来自浏览器、UniApp、SDK 或其它外部客户端的通用 FormEngine 请求,必须由服务端完成以下授权,V8 或前端代码不得自行模拟、放宽:
|
|
22
|
-
|
|
23
|
-
- HTTP 客户端显式传入 `_SysMenuId`(或兼容的 `ModuleEngineKey`)时,服务端默认进入**严格精确菜单授权**:校验该菜单真实存在、绑定的 `DiyTableId` 与目标表一致、当前用户有效角色拥有该菜单,以及当前操作的 `Read`、`Add`、`Edit`、`Del` 等权限。列表、写入、导入、导出显式传错、伪造或借用其它菜单 Id 必须失败关闭。
|
|
24
|
-
- 为兼容已发布项目中大量未传或保留过期 `_SysMenuId` 的旧版 PC/UniApp,普通业务表的唯一详情只要当前角色真实拥有至少一个直接绑定同表的菜单(或精确表级 `Read` 权限)即可读取,不应用菜单 `SqlWhere` / `SqlJoin`。该规则不能用于放宽列表、导入、导出或写入的菜单动作权限。
|
|
25
|
-
- 历史前端 V8 的无菜单 FormEngine 集合请求由后端根据“当前用户有效角色可访问的 `sys_menu`”推断目标表权限;多个菜单范围只有 Join 上下文一致时才可合并,否则失败关闭。推断必须使用后端授权快照,不能相信前端提交的角色、菜单列表或权限 JSON。确实没有菜单入口的 SDK/定制页面仍可按最小权限使用高级表权限;`diy_table.BindRole` 只做候选角色过滤,不能单独代替操作权限。
|
|
26
|
-
- 标准 PC 表单引擎会通过前端 FormEngine facade 给“当前表”的 V8 调用自动注入真实 `_SysMenuId`;V8 跨表调用故意保持无菜单,让后端按目标表的已授权菜单推断,禁止把当前主表菜单错误传播给其它表。
|
|
27
|
-
- 历史 PC/UniApp 的单表字段元数据可在菜单缺失/过期时回退到当前角色另一个引用同表的已授权菜单;`GetDiyFieldByDiyTables` 还可能批量提交“主表 + 关联表”,后端必须保留调用顺序,以第一张主表作为强制授权锚点。主表通过后,后续表逐张授权并过滤未授权表/保护表,不能让一张被拒绝的关联表拖垮已授权主表,也不能把被过滤表的字段、SQL 数据源或 V8 配置返回客户端。元数据兼容不授予数据行权限。
|
|
28
|
-
- `TableChild` 隐藏子菜单不要求存量角色逐个补授权。子表访问只能由后端在同一次请求中验证“已授权父菜单、父表 TableChild 字段配置、子菜单绑定、父记录可见范围、父键唯一性和子表外键”后委托,并把外键条件强制写入真实查询/写入;伪造 `_TableChildAuth`、跨父记录借用外键或脱离父表直接访问都必须失败。
|
|
29
|
-
- 菜单 `SqlWhere`、`SqlJoin` / `JoinTables` 数据范围必须在服务端形成的**真实列表、计数和导出查询**中执行,不能只用于界面展示或查询后过滤。单行详情只校验同表菜单访问权,不应用这些模块列表过滤;它们也不是行级写权限。
|
|
30
|
-
- 主表新增、修改、删除分别由当前角色的 `Add`、`Edit`、`Del` 权限控制;不得把查询 SqlWhere 追加到写入 SQL,也不得因为查询包含跨表 Join 拒绝已获授权的写入。需要“仅可修改本人数据”等业务限制时,在 `SubmitBeforeServerV8` 或专用接口引擎中以可信服务器代码校验,并统一写入 `TenantId`、负责人、创建人等归属字段。
|
|
31
|
-
- 导入、导出必须携带真实菜单上下文,并分别拥有 `Import`、`Export`;不能用 Table 级直接授权绕过。
|
|
32
|
-
- 平台表必须按后端 `PlatformResourceSecurity` 分级:账号/角色/权限、SaaS 配置、接口引擎、表字段元数据、任务、数据源、密钥和基础设施等管理员专用表,对 `Level < 9999` 的通用客户端 FormEngine 全操作硬拒绝;工作流、微服务/商店、蓝图和微应用运行元数据只允许显式授权后的 `Read/List`,写入仍硬拒绝;`mic_page/mic_print` 按真实菜单或 Table 的 `Read/Add/Edit/Del` 权限管理。
|
|
33
|
-
- 匿名读取/新增仅适用于 `diy_table` 明确开启匿名能力的普通业务表;上述三类平台表必须先于匿名开关拒绝。
|
|
34
|
-
- 角色增删改接口不能只相信 Token 缓存或前端禁用状态,必须覆盖请求中的 `_CurrentUser/OsClient`,并从租户主库复核活动用户、数据库 Level 与有效角色 Level。角色降级要先同步受影响用户 Level,再提升共享授权 `epoch`,避免旧令牌窗口;Postman 伪造 `_IsAdmin/Level/RoleIds` 必须失败。
|
|
35
|
-
- 权限 JSON、角色 Id 或菜单上下文解析失败时必须失败关闭;角色 Id 使用精确集合匹配,禁止 `Contains` 子串判断。
|
|
36
|
-
|
|
37
|
-
标准菜单模块继承菜单权限,不需要维护“角色 × 全部业务表”的巨大矩阵,也不能要求所有历史前端 V8 立即补传菜单 Id。新前端在当前表上下文应由平台 facade 自动携带真实菜单;历史无菜单请求继续由后端安全推断。【高级表权限】只用于确实没有任何菜单入口的定制页面/SDK,并按最小权限授予。
|
|
38
|
-
|
|
39
|
-
跨租户公共发现接口可以匿名,但返回值必须采用严格字段白名单,只返回公开标识和展示信息,禁止返回 `ApiKey`、上游 Endpoint、连接串或内部配置。例如官方 AI 中转模型清单应保持 `IsEnable=1`、`StopHttp=0`、`AllowAnonymous=1`,且只返回模型 Id、展示名等公开字段;将这类被其它租户服务器调用的发现接口误改为非匿名,会让所有消费者得到空模型列表或 `NoAuth`,属于破坏性配置变更。发布和升级后必须用无 Token 的真实 HTTP 请求验证返回字段与匿名状态。
|
|
40
|
-
|
|
41
|
-
> 后端接口引擎、后端表单 V8 和平台内部调用由服务端在构造参数时设置不可由 HTTP JSON 注入的 `_TrustedServerInvocation`,因此调用 `V8.FormEngine` 不要求 `_SysMenuId`。`_InvokeType:'Server'` 只是事件调用语义,不是外部客户端可用的授权开关;普通用户即使在请求中伪造这两个字段也不能成为可信调用。任何能让普通用户写 V8、接口引擎、任务或数据源配置的管理入口本身都必须限制为 `Level >= 9999`,否则会重新获得任意数据执行能力。
|
|
42
|
-
|
|
43
|
-
> 外层 `AddFormData` 的客户端菜单/`Add` 权限校验与事件内部执行权是两件事:前者防止无新增权限的用户进入事件,后者允许已经进入的 `SubmitBeforeServerV8` / `SubmitAfterServerV8` 像接口引擎一样在当前租户内执行复杂 SQL、跨表事务和其它表 CRUD。不要把客户端菜单范围再次套到服务器 V8 的嵌套调用。
|
|
44
|
-
|
|
45
|
-
### FormEngine 授权缓存与性能
|
|
46
|
-
|
|
47
|
-
授权不能在每次 FormEngine 请求中重复全表查询,也不能用只在单节点有效的永久静态字典。平台使用按 `OsClient` 隔离的 Redis 授权版本 `epoch`、用户级授权快照、短 TTL 的进程内 L1 与共享 Redis L2:
|
|
48
|
-
|
|
49
|
-
- 请求先读取当前租户 `epoch`,再按“租户 + epoch + 用户”读取授权快照;同一版本命中时复用有效用户、有效角色、菜单、表与操作权限。
|
|
50
|
-
- 正确菜单、无菜单候选范围和详情同表菜单访问都只读取一次缓存快照;详情不逐菜单执行数据范围探测。不得把详情的同表菜单兼容扩展为无范围列表或批量查询。
|
|
51
|
-
- 冷加载必须从主库读取 `sys_user`、`sys_role`、`sys_rolelimit`、`sys_menu` 等授权事实,避免只读副本延迟把已撤销权限重新缓存。
|
|
52
|
-
- 用户状态/级别/角色、角色状态、菜单绑定/数据范围、角色菜单或高级表权限发生变化时,必须在事务成功后递增共享 Redis `epoch`。所有 API 节点看到新版本后自然放弃旧快照,不依赖粘性会话或逐节点重启。
|
|
53
|
-
- L1 只做短时性能优化,允许丢失;L2 和 `epoch` 才负责多节点共享版本。缓存失效异常时应缩短使用窗口并失败关闭敏感操作,不能无限沿用旧权限。
|
|
54
|
-
- 快照 Key 必须包含独立的“序列化契约版本”。新增 `UserLevel`、`IsActiveUser` 等安全字段或改变字段语义时提升版本,让 Redis 中跨重启、滚动升级遗留的旧 JSON 立即失效,禁止缺失字段按 `0/false` 反序列化后误拒绝有效管理员或误放普通用户。
|
|
55
|
-
- 已通过 `Level >= 9999` 校验的表单设计器批量写 `diy_field` 时,应在外层只授权一次,并校验所有字段都属于同一 `TableId`;随后在同一事务内更新元数据,批次结束后只清一次缓存/版本。不要让每个字段重新进入通用 FormEngine 授权、V8、日志和缓存管线。`AddDiyField/AddField` 等单字段能力仍须传递经服务端确认的管理员上下文;升级程序等无 HTTP 用户的可信任务必须构造强类型参数并设置不可由 JSON 绑定的 `_TrustedServerInvocation`。
|
|
56
|
-
|
|
57
|
-
### 接口引擎中校验当前用户
|
|
58
|
-
|
|
59
|
-
```javascript
|
|
60
|
-
// 只允许自己查看自己的数据
|
|
6
|
+
## 2. 权限校验
|
|
7
|
+
|
|
8
|
+
### DiyToken 是平台会话与权限入口,不替换为 ASP.NET Identity
|
|
9
|
+
|
|
10
|
+
DiyToken 与 `sys_user`、`OsClient`、终端 `did`、共享 Redis 登录态、角色/部门、菜单动作、表权限、数据范围和接口引擎共同构成吾码认证授权体系。它不是“只有一个 Token 字符串”的简化登录:
|
|
11
|
+
|
|
12
|
+
- 登录、SSO、OAuth、Passkey、人脸或访问密钥验证成功后,统一签发/兑换 DiyToken。
|
|
13
|
+
- Token 认证只证明用户和租户;服务端仍按资源、动作和数据范围授权,前端按钮不可代替。
|
|
14
|
+
- 用户停用、角色变化、单终端/全部终端吊销和 Token 轮换都依赖共享会话事实,不依赖单机内存。
|
|
15
|
+
- ASP.NET Identity 可以作为外部身份源的适配参考,但不能整体替换吾码的租户、低代码权限、V8 和在线终端协议。
|
|
16
|
+
|
|
17
|
+
开发新登录入口时只实现“验证身份 -> 获取仍启用的 `sys_user` -> 签发 DiyToken”,禁止并行创建第二套用户、角色、权限 Token 或会话有效期规则。
|
|
18
|
+
|
|
19
|
+
### 平台 FormEngine 授权边界
|
|
20
|
+
|
|
21
|
+
**Token 只完成身份认证,不是任意表的访问凭证。** 来自浏览器、UniApp、SDK 或其它外部客户端的通用 FormEngine 请求,必须由服务端完成以下授权,V8 或前端代码不得自行模拟、放宽:
|
|
22
|
+
|
|
23
|
+
- HTTP 客户端显式传入 `_SysMenuId`(或兼容的 `ModuleEngineKey`)时,服务端默认进入**严格精确菜单授权**:校验该菜单真实存在、绑定的 `DiyTableId` 与目标表一致、当前用户有效角色拥有该菜单,以及当前操作的 `Read`、`Add`、`Edit`、`Del` 等权限。列表、写入、导入、导出显式传错、伪造或借用其它菜单 Id 必须失败关闭。
|
|
24
|
+
- 为兼容已发布项目中大量未传或保留过期 `_SysMenuId` 的旧版 PC/UniApp,普通业务表的唯一详情只要当前角色真实拥有至少一个直接绑定同表的菜单(或精确表级 `Read` 权限)即可读取,不应用菜单 `SqlWhere` / `SqlJoin`。该规则不能用于放宽列表、导入、导出或写入的菜单动作权限。
|
|
25
|
+
- 历史前端 V8 的无菜单 FormEngine 集合请求由后端根据“当前用户有效角色可访问的 `sys_menu`”推断目标表权限;多个菜单范围只有 Join 上下文一致时才可合并,否则失败关闭。推断必须使用后端授权快照,不能相信前端提交的角色、菜单列表或权限 JSON。确实没有菜单入口的 SDK/定制页面仍可按最小权限使用高级表权限;`diy_table.BindRole` 只做候选角色过滤,不能单独代替操作权限。
|
|
26
|
+
- 标准 PC 表单引擎会通过前端 FormEngine facade 给“当前表”的 V8 调用自动注入真实 `_SysMenuId`;V8 跨表调用故意保持无菜单,让后端按目标表的已授权菜单推断,禁止把当前主表菜单错误传播给其它表。
|
|
27
|
+
- 历史 PC/UniApp 的单表字段元数据可在菜单缺失/过期时回退到当前角色另一个引用同表的已授权菜单;`GetDiyFieldByDiyTables` 还可能批量提交“主表 + 关联表”,后端必须保留调用顺序,以第一张主表作为强制授权锚点。主表通过后,后续表逐张授权并过滤未授权表/保护表,不能让一张被拒绝的关联表拖垮已授权主表,也不能把被过滤表的字段、SQL 数据源或 V8 配置返回客户端。元数据兼容不授予数据行权限。
|
|
28
|
+
- `TableChild` 隐藏子菜单不要求存量角色逐个补授权。子表访问只能由后端在同一次请求中验证“已授权父菜单、父表 TableChild 字段配置、子菜单绑定、父记录可见范围、父键唯一性和子表外键”后委托,并把外键条件强制写入真实查询/写入;伪造 `_TableChildAuth`、跨父记录借用外键或脱离父表直接访问都必须失败。
|
|
29
|
+
- 菜单 `SqlWhere`、`SqlJoin` / `JoinTables` 数据范围必须在服务端形成的**真实列表、计数和导出查询**中执行,不能只用于界面展示或查询后过滤。单行详情只校验同表菜单访问权,不应用这些模块列表过滤;它们也不是行级写权限。
|
|
30
|
+
- 主表新增、修改、删除分别由当前角色的 `Add`、`Edit`、`Del` 权限控制;不得把查询 SqlWhere 追加到写入 SQL,也不得因为查询包含跨表 Join 拒绝已获授权的写入。需要“仅可修改本人数据”等业务限制时,在 `SubmitBeforeServerV8` 或专用接口引擎中以可信服务器代码校验,并统一写入 `TenantId`、负责人、创建人等归属字段。
|
|
31
|
+
- 导入、导出必须携带真实菜单上下文,并分别拥有 `Import`、`Export`;不能用 Table 级直接授权绕过。
|
|
32
|
+
- 平台表必须按后端 `PlatformResourceSecurity` 分级:账号/角色/权限、SaaS 配置、接口引擎、表字段元数据、任务、数据源、密钥和基础设施等管理员专用表,对 `Level < 9999` 的通用客户端 FormEngine 全操作硬拒绝;工作流、微服务/商店、蓝图和微应用运行元数据只允许显式授权后的 `Read/List`,写入仍硬拒绝;`mic_page/mic_print` 按真实菜单或 Table 的 `Read/Add/Edit/Del` 权限管理。
|
|
33
|
+
- 匿名读取/新增仅适用于 `diy_table` 明确开启匿名能力的普通业务表;上述三类平台表必须先于匿名开关拒绝。
|
|
34
|
+
- 角色增删改接口不能只相信 Token 缓存或前端禁用状态,必须覆盖请求中的 `_CurrentUser/OsClient`,并从租户主库复核活动用户、数据库 Level 与有效角色 Level。角色降级要先同步受影响用户 Level,再提升共享授权 `epoch`,避免旧令牌窗口;Postman 伪造 `_IsAdmin/Level/RoleIds` 必须失败。
|
|
35
|
+
- 权限 JSON、角色 Id 或菜单上下文解析失败时必须失败关闭;角色 Id 使用精确集合匹配,禁止 `Contains` 子串判断。
|
|
36
|
+
|
|
37
|
+
标准菜单模块继承菜单权限,不需要维护“角色 × 全部业务表”的巨大矩阵,也不能要求所有历史前端 V8 立即补传菜单 Id。新前端在当前表上下文应由平台 facade 自动携带真实菜单;历史无菜单请求继续由后端安全推断。【高级表权限】只用于确实没有任何菜单入口的定制页面/SDK,并按最小权限授予。
|
|
38
|
+
|
|
39
|
+
跨租户公共发现接口可以匿名,但返回值必须采用严格字段白名单,只返回公开标识和展示信息,禁止返回 `ApiKey`、上游 Endpoint、连接串或内部配置。例如官方 AI 中转模型清单应保持 `IsEnable=1`、`StopHttp=0`、`AllowAnonymous=1`,且只返回模型 Id、展示名等公开字段;将这类被其它租户服务器调用的发现接口误改为非匿名,会让所有消费者得到空模型列表或 `NoAuth`,属于破坏性配置变更。发布和升级后必须用无 Token 的真实 HTTP 请求验证返回字段与匿名状态。
|
|
40
|
+
|
|
41
|
+
> 后端接口引擎、后端表单 V8 和平台内部调用由服务端在构造参数时设置不可由 HTTP JSON 注入的 `_TrustedServerInvocation`,因此调用 `V8.FormEngine` 不要求 `_SysMenuId`。`_InvokeType:'Server'` 只是事件调用语义,不是外部客户端可用的授权开关;普通用户即使在请求中伪造这两个字段也不能成为可信调用。任何能让普通用户写 V8、接口引擎、任务或数据源配置的管理入口本身都必须限制为 `Level >= 9999`,否则会重新获得任意数据执行能力。
|
|
42
|
+
|
|
43
|
+
> 外层 `AddFormData` 的客户端菜单/`Add` 权限校验与事件内部执行权是两件事:前者防止无新增权限的用户进入事件,后者允许已经进入的 `SubmitBeforeServerV8` / `SubmitAfterServerV8` 像接口引擎一样在当前租户内执行复杂 SQL、跨表事务和其它表 CRUD。不要把客户端菜单范围再次套到服务器 V8 的嵌套调用。
|
|
44
|
+
|
|
45
|
+
### FormEngine 授权缓存与性能
|
|
46
|
+
|
|
47
|
+
授权不能在每次 FormEngine 请求中重复全表查询,也不能用只在单节点有效的永久静态字典。平台使用按 `OsClient` 隔离的 Redis 授权版本 `epoch`、用户级授权快照、短 TTL 的进程内 L1 与共享 Redis L2:
|
|
48
|
+
|
|
49
|
+
- 请求先读取当前租户 `epoch`,再按“租户 + epoch + 用户”读取授权快照;同一版本命中时复用有效用户、有效角色、菜单、表与操作权限。
|
|
50
|
+
- 正确菜单、无菜单候选范围和详情同表菜单访问都只读取一次缓存快照;详情不逐菜单执行数据范围探测。不得把详情的同表菜单兼容扩展为无范围列表或批量查询。
|
|
51
|
+
- 冷加载必须从主库读取 `sys_user`、`sys_role`、`sys_rolelimit`、`sys_menu` 等授权事实,避免只读副本延迟把已撤销权限重新缓存。
|
|
52
|
+
- 用户状态/级别/角色、角色状态、菜单绑定/数据范围、角色菜单或高级表权限发生变化时,必须在事务成功后递增共享 Redis `epoch`。所有 API 节点看到新版本后自然放弃旧快照,不依赖粘性会话或逐节点重启。
|
|
53
|
+
- L1 只做短时性能优化,允许丢失;L2 和 `epoch` 才负责多节点共享版本。缓存失效异常时应缩短使用窗口并失败关闭敏感操作,不能无限沿用旧权限。
|
|
54
|
+
- 快照 Key 必须包含独立的“序列化契约版本”。新增 `UserLevel`、`IsActiveUser` 等安全字段或改变字段语义时提升版本,让 Redis 中跨重启、滚动升级遗留的旧 JSON 立即失效,禁止缺失字段按 `0/false` 反序列化后误拒绝有效管理员或误放普通用户。
|
|
55
|
+
- 已通过 `Level >= 9999` 校验的表单设计器批量写 `diy_field` 时,应在外层只授权一次,并校验所有字段都属于同一 `TableId`;随后在同一事务内更新元数据,批次结束后只清一次缓存/版本。不要让每个字段重新进入通用 FormEngine 授权、V8、日志和缓存管线。`AddDiyField/AddField` 等单字段能力仍须传递经服务端确认的管理员上下文;升级程序等无 HTTP 用户的可信任务必须构造强类型参数并设置不可由 JSON 绑定的 `_TrustedServerInvocation`。
|
|
56
|
+
|
|
57
|
+
### 接口引擎中校验当前用户
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// 只允许自己查看自己的数据
|
|
61
61
|
if (!V8.CurrentUser || !V8.CurrentUser.Id) {
|
|
62
62
|
return { Code: -1, Msg: '未登录' };
|
|
63
63
|
}
|
|
@@ -104,78 +104,78 @@ var result = V8.FormEngine.GetTableData('Order', {
|
|
|
104
104
|
|
|
105
105
|
<!-- /microi-progressive:chunk -->
|
|
106
106
|
<!-- microi-progressive:chunk id=v8-security-006 sha256=ba49a1e5ea27c6cca4c78c6ee3e95a5e41032bb08f6813753bc8aa855ddfc832 -->
|
|
107
|
-
## 5. 防重复提交
|
|
108
|
-
|
|
109
|
-
前端禁用按钮或普通 Cache 的 `Exists → Set` 只能改善体验,不能保证业务只执行一次。写操作必须接收稳定幂等键,并通过数据库唯一约束、条件更新或状态机原子落库;接口引擎可再配置 `LockKey` 降低并发,但锁不能代替业务幂等。
|
|
110
|
-
|
|
111
|
-
```javascript
|
|
112
|
-
var requestId = String(V8.Param.RequestId || '');
|
|
113
|
-
if (!/^[A-Za-z0-9_-]{8,100}$/.test(requestId)) {
|
|
114
|
-
return { Code: 0, Msg: '缺少合法幂等键' };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// IdempotencyKey 在数据库中建立唯一索引;重复请求读取并返回已有结果
|
|
118
|
-
var old = V8.FormEngine.GetFormData('Order', {
|
|
119
|
-
_Where: [['IdempotencyKey', '=', requestId]]
|
|
120
|
-
});
|
|
121
|
-
if (old.Code === 1 && old.Data) {
|
|
122
|
-
return { Code: 1, Data: old.Data, Msg: '重复请求已复用原结果' };
|
|
123
|
-
}
|
|
124
|
-
// 最终仍以唯一约束处理并发竞态,不以这次预查询作为安全保证
|
|
125
|
-
```
|
|
107
|
+
## 5. 防重复提交
|
|
108
|
+
|
|
109
|
+
前端禁用按钮或普通 Cache 的 `Exists → Set` 只能改善体验,不能保证业务只执行一次。写操作必须接收稳定幂等键,并通过数据库唯一约束、条件更新或状态机原子落库;接口引擎可再配置 `LockKey` 降低并发,但锁不能代替业务幂等。
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
var requestId = String(V8.Param.RequestId || '');
|
|
113
|
+
if (!/^[A-Za-z0-9_-]{8,100}$/.test(requestId)) {
|
|
114
|
+
return { Code: 0, Msg: '缺少合法幂等键' };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// IdempotencyKey 在数据库中建立唯一索引;重复请求读取并返回已有结果
|
|
118
|
+
var old = V8.FormEngine.GetFormData('Order', {
|
|
119
|
+
_Where: [['IdempotencyKey', '=', requestId]]
|
|
120
|
+
});
|
|
121
|
+
if (old.Code === 1 && old.Data) {
|
|
122
|
+
return { Code: 1, Data: old.Data, Msg: '重复请求已复用原结果' };
|
|
123
|
+
}
|
|
124
|
+
// 最终仍以唯一约束处理并发竞态,不以这次预查询作为安全保证
|
|
125
|
+
```
|
|
126
126
|
|
|
127
127
|
<!-- /microi-progressive:chunk -->
|
|
128
128
|
<!-- microi-progressive:chunk id=v8-security-007 sha256=60297c515c7c7b6e35cd871ab2fd7c772f8b227dfcefa0419868943213556c06 -->
|
|
129
129
|
## 6. 敏感数据
|
|
130
130
|
|
|
131
|
-
### 密码与认证
|
|
132
|
-
|
|
133
|
-
业务 V8 禁止自行查询 `sys_user`、保存密码或用 MD5/SHA1/SHA256 直接哈希密码。统一使用平台登录、重置密码和管理员用户管理接口;新密码存储必须由后端使用带盐、可调成本的专用密码哈希,并支持版本与轮换。登录 RSA 只隐藏传输报文中的明文外观,不能替代 HTTPS,也不能用于密码存储。
|
|
134
|
-
|
|
135
|
-
存量 `PwdEncode=DES` 的兼容例外只能位于 `[PlatformAdminOnly]` 的 `GetSysUserPassword`:还要拒绝访问密钥会话、按当前 `OsClient` 和准确用户 Id 查询、用“解密后重新加密等于原密文”验证结果、返回 `no-store` 并写不含明文的安全审计。`PwdEncode=V8` 不假定可逆。不得把该能力暴露给 FormEngine、V8、普通角色或匿名端点。
|
|
136
|
-
|
|
137
|
-
### 可逆业务秘密:允许加密存储和授权显示
|
|
138
|
-
|
|
139
|
-
设备口令、第三方业务账号密码、客户明确要求再次显示的字段可以使用 `V8.EncryptHelper.DESEncode/DESDecode` 兼容机制,但必须与登录密码、支付私钥、`AuthSecret` 和基础设施密钥分开:
|
|
140
|
-
|
|
141
|
-
```javascript
|
|
142
|
-
// 保存:只在可信后端执行
|
|
143
|
-
V8.Form.SecretCipher = V8.EncryptHelper.DESEncode(String(V8.Form.SecretPlain || ''));
|
|
144
|
-
V8.NotSaveField.push('SecretPlain');
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
显示明文必须设计为独立后端动作,并同时做到:
|
|
148
|
-
|
|
149
|
-
- 校验当前 DiyToken、准确 `OsClient`、当前用户真实角色/菜单/业务权限;高风险场景再要求下面的一次性强身份票据。
|
|
150
|
-
- 只解密一条明确记录,不向列表、批量导出、通用 FormEngine、匿名或访问密钥会话提供解密器。
|
|
151
|
-
- 只记录操作者、目标 Id、用途、结果和时间,禁止日志/审计/通知/缓存保存明文;HTTP 响应设置 `no-store`。
|
|
152
|
-
- 页面默认掩码,点击显示需二次确认,失焦/超时/路由离开后清除;禁止复制到 URL、LocalStorage 或前端日志。
|
|
153
|
-
- DES 是存量兼容格式。新高价值秘密优先由可信 C# 原子能力使用带版本的现代认证加密和集中密钥管理,V8 仍只编排授权显示,不能获取主密钥。
|
|
154
|
-
|
|
155
|
-
### Passkey、Authenticator、人脸与敏感操作票据
|
|
156
|
-
|
|
157
|
-
前端 V8 使用 `V8.Identity.Verify({Purpose,ActionHash,Method,Code})` 完成人机交互;`Method=Totp` 时 `Code` 是用户当前看到的 6 位动态口令。后端必须从数据库重读权威业务字段、按稳定版本/顺序重算 `ActionHash`,再消费票据:
|
|
158
|
-
|
|
159
|
-
```javascript
|
|
160
|
-
var actionHash = V8.EncryptHelper.Sha256Hex(canonicalCommand);
|
|
161
|
-
var verified = V8.Method.ConsumeIdentityVerificationTicket({
|
|
162
|
-
Ticket: V8.Param.IdentityVerificationTicket,
|
|
163
|
-
Purpose: 'RevealBusinessSecret',
|
|
164
|
-
ActionHash: actionHash
|
|
165
|
-
});
|
|
166
|
-
if (verified.Code !== 1) return verified;
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
- Ticket 绑定 `OsClient + UserId + Purpose + ActionHash`,共享 Redis 保存两分钟并使用原子 `GETDEL`,只能消费一次。
|
|
170
|
-
- 前端提交的摘要、`Verified=true`、认证器名称或方法不能作为授权事实;访问密钥会话不能使用。
|
|
171
|
-
- 票据只证明近期强身份,不代替菜单/表/行权限、业务状态机、事务、幂等或审计。
|
|
172
|
-
- 设备指纹/Face ID/Windows Hello 优先走 WebAuthn/Passkey,不需要模型 Docker;只有严格服务端人脸/活体检测才通过 `Microi Face Gateway v1` 接入独立服务。
|
|
173
|
-
- 标准 TOTP Authenticator 不需要 Docker,但 6 位码不能单独标识账号:登录时仍需账号;密钥只以租户绑定的认证密文保存,并用共享限流和已接受计数器阻止暴力尝试与重放。
|
|
174
|
-
- 每个 Passkey/TOTP 分别保存 `AllowPasswordlessLogin` 与 `AllowStepUp`;登录和票据签发都必须服务端重新读取策略,不能只依赖个人中心开关的前端状态。
|
|
175
|
-
- Gitee、微信、GitHub 等外部身份只允许登录已绑定的吾码用户;禁止按邮箱/昵称自动合并账号。OAuth state 与登录票据保存在共享 Redis、单次消费并绑定 `OsClient`/Provider/Origin;固定端点白名单,ClientSecret 只由可信后端读取,最终仍签发 DiyToken。
|
|
176
|
-
- 官方升级通过 `app.microi.saas-engine` 应用包幂等安装身份表、`mci_system_setting`、`mci_user_external_identity`、默认设置、个人中心和系统设置微服务。默认设置按 `ConfigKey + InsertIfMissing` 补齐,不覆盖 `ValueSource=Tenant` 的租户值;Passkey、TOTP、总开关和改密步进验证默认开启,严格人脸及各外部 Provider 默认关闭。
|
|
177
|
-
- 完整表、SaaS 字段、API 和启用顺序见 `microi.doc/docs/doc/more/identity-verification.md`。
|
|
178
|
-
- 身份验证 HTTP 控制面位于 `/api/identityverification/`,外部登录位于 `/api/externallogin/`,动态设置位于 `/api/tenantsystemsettings/`;业务页面优先使用 `V8.Identity` 或官方个人中心,不要自行复制 WebAuthn/OAuth 协议代码。
|
|
131
|
+
### 密码与认证
|
|
132
|
+
|
|
133
|
+
业务 V8 禁止自行查询 `sys_user`、保存密码或用 MD5/SHA1/SHA256 直接哈希密码。统一使用平台登录、重置密码和管理员用户管理接口;新密码存储必须由后端使用带盐、可调成本的专用密码哈希,并支持版本与轮换。登录 RSA 只隐藏传输报文中的明文外观,不能替代 HTTPS,也不能用于密码存储。
|
|
134
|
+
|
|
135
|
+
存量 `PwdEncode=DES` 的兼容例外只能位于 `[PlatformAdminOnly]` 的 `GetSysUserPassword`:还要拒绝访问密钥会话、按当前 `OsClient` 和准确用户 Id 查询、用“解密后重新加密等于原密文”验证结果、返回 `no-store` 并写不含明文的安全审计。`PwdEncode=V8` 不假定可逆。不得把该能力暴露给 FormEngine、V8、普通角色或匿名端点。
|
|
136
|
+
|
|
137
|
+
### 可逆业务秘密:允许加密存储和授权显示
|
|
138
|
+
|
|
139
|
+
设备口令、第三方业务账号密码、客户明确要求再次显示的字段可以使用 `V8.EncryptHelper.DESEncode/DESDecode` 兼容机制,但必须与登录密码、支付私钥、`AuthSecret` 和基础设施密钥分开:
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
// 保存:只在可信后端执行
|
|
143
|
+
V8.Form.SecretCipher = V8.EncryptHelper.DESEncode(String(V8.Form.SecretPlain || ''));
|
|
144
|
+
V8.NotSaveField.push('SecretPlain');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
显示明文必须设计为独立后端动作,并同时做到:
|
|
148
|
+
|
|
149
|
+
- 校验当前 DiyToken、准确 `OsClient`、当前用户真实角色/菜单/业务权限;高风险场景再要求下面的一次性强身份票据。
|
|
150
|
+
- 只解密一条明确记录,不向列表、批量导出、通用 FormEngine、匿名或访问密钥会话提供解密器。
|
|
151
|
+
- 只记录操作者、目标 Id、用途、结果和时间,禁止日志/审计/通知/缓存保存明文;HTTP 响应设置 `no-store`。
|
|
152
|
+
- 页面默认掩码,点击显示需二次确认,失焦/超时/路由离开后清除;禁止复制到 URL、LocalStorage 或前端日志。
|
|
153
|
+
- DES 是存量兼容格式。新高价值秘密优先由可信 C# 原子能力使用带版本的现代认证加密和集中密钥管理,V8 仍只编排授权显示,不能获取主密钥。
|
|
154
|
+
|
|
155
|
+
### Passkey、Authenticator、人脸与敏感操作票据
|
|
156
|
+
|
|
157
|
+
前端 V8 使用 `V8.Identity.Verify({Purpose,ActionHash,Method,Code})` 完成人机交互;`Method=Totp` 时 `Code` 是用户当前看到的 6 位动态口令。后端必须从数据库重读权威业务字段、按稳定版本/顺序重算 `ActionHash`,再消费票据:
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
var actionHash = V8.EncryptHelper.Sha256Hex(canonicalCommand);
|
|
161
|
+
var verified = V8.Method.ConsumeIdentityVerificationTicket({
|
|
162
|
+
Ticket: V8.Param.IdentityVerificationTicket,
|
|
163
|
+
Purpose: 'RevealBusinessSecret',
|
|
164
|
+
ActionHash: actionHash
|
|
165
|
+
});
|
|
166
|
+
if (verified.Code !== 1) return verified;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- Ticket 绑定 `OsClient + UserId + Purpose + ActionHash`,共享 Redis 保存两分钟并使用原子 `GETDEL`,只能消费一次。
|
|
170
|
+
- 前端提交的摘要、`Verified=true`、认证器名称或方法不能作为授权事实;访问密钥会话不能使用。
|
|
171
|
+
- 票据只证明近期强身份,不代替菜单/表/行权限、业务状态机、事务、幂等或审计。
|
|
172
|
+
- 设备指纹/Face ID/Windows Hello 优先走 WebAuthn/Passkey,不需要模型 Docker;只有严格服务端人脸/活体检测才通过 `Microi Face Gateway v1` 接入独立服务。
|
|
173
|
+
- 标准 TOTP Authenticator 不需要 Docker,但 6 位码不能单独标识账号:登录时仍需账号;密钥只以租户绑定的认证密文保存,并用共享限流和已接受计数器阻止暴力尝试与重放。
|
|
174
|
+
- 每个 Passkey/TOTP 分别保存 `AllowPasswordlessLogin` 与 `AllowStepUp`;登录和票据签发都必须服务端重新读取策略,不能只依赖个人中心开关的前端状态。
|
|
175
|
+
- Gitee、微信、GitHub 等外部身份只允许登录已绑定的吾码用户;禁止按邮箱/昵称自动合并账号。OAuth state 与登录票据保存在共享 Redis、单次消费并绑定 `OsClient`/Provider/Origin;固定端点白名单,ClientSecret 只由可信后端读取,最终仍签发 DiyToken。
|
|
176
|
+
- 官方升级通过 `app.microi.saas-engine` 应用包幂等安装身份表、`mci_system_setting`、`mci_user_external_identity`、默认设置、个人中心和系统设置微服务。默认设置按 `ConfigKey + InsertIfMissing` 补齐,不覆盖 `ValueSource=Tenant` 的租户值;Passkey、TOTP、总开关和改密步进验证默认开启,严格人脸及各外部 Provider 默认关闭。
|
|
177
|
+
- 完整表、SaaS 字段、API 和启用顺序见 `microi.doc/docs/doc/more/identity-verification.md`。
|
|
178
|
+
- 身份验证 HTTP 控制面位于 `/api/identityverification/`,外部登录位于 `/api/externallogin/`,动态设置位于 `/api/tenantsystemsettings/`;业务页面优先使用 `V8.Identity` 或官方个人中心,不要自行复制 WebAuthn/OAuth 协议代码。
|
|
179
179
|
|
|
180
180
|
### 脱敏返回
|
|
181
181
|
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: v8-sql-query
|
|
3
|
-
description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Where 或 V8.Db.FromSql,处理参数化查询、联表、聚合并避免 SQL 注入。
|
|
3
|
+
description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Where 或 V8.Db.FromSql,处理参数化查询、联表、聚合并避免 SQL 注入。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
7
|
|
|
8
8
|
# Microi V8 安全 SQL 查询
|
|
9
9
|
|
|
10
|
-
你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
|
|
11
|
-
|
|
12
|
-
## 性能门禁(必须执行)
|
|
13
|
-
|
|
14
|
-
写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
|
|
15
|
-
|
|
16
|
-
- 禁止在 `for` / `while` / `forEach` / `map` 循环内调用 `V8.FormEngine.GetFormData`、`GetTableData`、`V8.Db.FromSql`、`V8.ApiEngine.Run` 或 `V8.Http.*`。确实无法避免时,必须先说明原因,并加分页、缓存或限流。
|
|
17
|
-
- 多条 Id、编码、外键查询必须一次性用 `_Where: [['Id','In', ids]]`、SQL `IN`、JOIN 或聚合查询取回,再用内存字典映射。
|
|
18
|
-
- 需要父子、主从、用户/部门/角色名称映射时,先批量取关联表,只保留 `_SelectFields` 必要列,不要逐行查名称。
|
|
19
|
-
- 统计、计数、汇总优先让数据库一次 `GROUP BY` / `COUNT` / `SUM` 完成,不要把大表全部拉到 V8 里循环统计。
|
|
20
|
-
- 列表接口必须限制 `_PageSize`,管理端默认不要超过 100,导出或批处理必须显式分批。
|
|
21
|
-
- 每次查询都要写 `_SelectFields` 或明确 SQL 字段列表,禁止 `SELECT *` 用在大表、接口列表、循环前置查询中。
|
|
22
|
-
- 外部 HTTP、翻译、短信、AI 等慢调用不能放在数据库事务和大循环中;要么异步队列,要么批量预处理并设置超时。
|
|
23
|
-
- 返回前自检一次:数据库访问次数应与数据量无关或近似常数级,不能随着行数线性增长为 N 次查询。
|
|
24
|
-
|
|
25
|
-
## 首选:V8.FormEngine + _Where(自动防注入)
|
|
10
|
+
你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
|
|
11
|
+
|
|
12
|
+
## 性能门禁(必须执行)
|
|
13
|
+
|
|
14
|
+
写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
|
|
15
|
+
|
|
16
|
+
- 禁止在 `for` / `while` / `forEach` / `map` 循环内调用 `V8.FormEngine.GetFormData`、`GetTableData`、`V8.Db.FromSql`、`V8.ApiEngine.Run` 或 `V8.Http.*`。确实无法避免时,必须先说明原因,并加分页、缓存或限流。
|
|
17
|
+
- 多条 Id、编码、外键查询必须一次性用 `_Where: [['Id','In', ids]]`、SQL `IN`、JOIN 或聚合查询取回,再用内存字典映射。
|
|
18
|
+
- 需要父子、主从、用户/部门/角色名称映射时,先批量取关联表,只保留 `_SelectFields` 必要列,不要逐行查名称。
|
|
19
|
+
- 统计、计数、汇总优先让数据库一次 `GROUP BY` / `COUNT` / `SUM` 完成,不要把大表全部拉到 V8 里循环统计。
|
|
20
|
+
- 列表接口必须限制 `_PageSize`,管理端默认不要超过 100,导出或批处理必须显式分批。
|
|
21
|
+
- 每次查询都要写 `_SelectFields` 或明确 SQL 字段列表,禁止 `SELECT *` 用在大表、接口列表、循环前置查询中。
|
|
22
|
+
- 外部 HTTP、翻译、短信、AI 等慢调用不能放在数据库事务和大循环中;要么异步队列,要么批量预处理并设置超时。
|
|
23
|
+
- 返回前自检一次:数据库访问次数应与数据量无关或近似常数级,不能随着行数线性增长为 N 次查询。
|
|
24
|
+
|
|
25
|
+
## 首选:V8.FormEngine + _Where(自动防注入)
|
|
26
26
|
|
|
27
27
|
`_Where` 是参数化查询语法,自动防 SQL 注入,**永远优先使用**。
|
|
28
28
|
|
|
@@ -75,57 +75,57 @@ var newWhere = V8.Method.ParseWhere(V8.Param._Where);
|
|
|
75
75
|
V8.FormEngine.GetTableData('Table', { _Where: newWhere });
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
|
|
79
|
-
|
|
80
|
-
> **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
|
|
81
|
-
|
|
82
|
-
> **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
// ❌ 错误形态:不要把动态值作为 FromSql 的第二个参数传入
|
|
86
|
-
|
|
87
|
-
// ✅ 优先:改用 FormEngine(单表增删改查都优先这样写)
|
|
88
|
-
V8.FormEngine.UptFormData('t', { Id: id, A: val1, B: val2 });
|
|
89
|
-
|
|
90
|
-
// ✅ 必须用原生 SQL 时:FromSql 只传 SQL,参数用 AddInParameter
|
|
91
|
-
V8.Db.FromSql("UPDATE t SET A=@p0, B=@p1 WHERE Id=@p2")
|
|
92
|
-
.AddInParameter("@p0", val1)
|
|
93
|
-
.AddInParameter("@p1", val2)
|
|
94
|
-
.AddInParameter("@p2", id)
|
|
95
|
-
.ExecuteNonQuery();
|
|
96
|
-
```
|
|
78
|
+
## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
|
|
79
|
+
|
|
80
|
+
> **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
|
|
81
|
+
|
|
82
|
+
> **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
// ❌ 错误形态:不要把动态值作为 FromSql 的第二个参数传入
|
|
86
|
+
|
|
87
|
+
// ✅ 优先:改用 FormEngine(单表增删改查都优先这样写)
|
|
88
|
+
V8.FormEngine.UptFormData('t', { Id: id, A: val1, B: val2 });
|
|
89
|
+
|
|
90
|
+
// ✅ 必须用原生 SQL 时:FromSql 只传 SQL,参数用 AddInParameter
|
|
91
|
+
V8.Db.FromSql("UPDATE t SET A=@p0, B=@p1 WHERE Id=@p2")
|
|
92
|
+
.AddInParameter("@p0", val1)
|
|
93
|
+
.AddInParameter("@p1", val2)
|
|
94
|
+
.AddInParameter("@p2", id)
|
|
95
|
+
.ExecuteNonQuery();
|
|
96
|
+
```
|
|
97
97
|
|
|
98
98
|
当 `_Where` 无法满足复杂查询(多表 JOIN、子查询、聚合统计)时使用 `V8.Db`:
|
|
99
99
|
|
|
100
100
|
```javascript
|
|
101
|
-
// ✅ 安全:使用 @p0, @p1 占位符,并用 AddInParameter 绑定
|
|
102
|
-
var list = V8.Db.FromSql(
|
|
103
|
-
'SELECT a.Id, a.Name, b.OrderCount FROM Customer a LEFT JOIN (SELECT CustomerId, COUNT(*) OrderCount FROM OrderHeader GROUP BY CustomerId) b ON a.Id = b.CustomerId WHERE a.Status = @p0'
|
|
104
|
-
).AddInParameter("@p0", 1).ToArray();
|
|
105
|
-
|
|
106
|
-
// ✅ 安全:多个参数
|
|
107
|
-
var row = V8.Db.FromSql(
|
|
108
|
-
'SELECT * FROM SysUser WHERE Account = @p0 AND DeptId = @p1'
|
|
109
|
-
).AddInParameter("@p0", V8.Param.account)
|
|
110
|
-
.AddInParameter("@p1", V8.Param.deptId)
|
|
111
|
-
.First();
|
|
112
|
-
|
|
113
|
-
// 统计
|
|
114
|
-
var count = V8.Db.FromSql(
|
|
115
|
-
'SELECT COUNT(*) FROM OrderHeader WHERE Status = @p0 AND CreateTime >= @p1'
|
|
116
|
-
).AddInParameter("@p0", 1)
|
|
117
|
-
.AddInParameter("@p1", V8.Param.startDate)
|
|
118
|
-
.ToScalar();
|
|
119
|
-
|
|
120
|
-
// 非查询(UPDATE / INSERT / DELETE)
|
|
121
|
-
V8.Db.FromSql(
|
|
122
|
-
'UPDATE SysUser SET LastLoginTime = @p0 WHERE Id = @p1'
|
|
123
|
-
).AddInParameter("@p0", DateNow('yyyy-MM-dd HH:mm:ss'))
|
|
124
|
-
.AddInParameter("@p1", V8.CurrentUser.Id)
|
|
125
|
-
.ExecuteNonQuery();
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
> 老版本兼容:要把维护接口复制到旧部署时,不要假定 `DateNow` 或 `System.DateTime.Now.ToString(...)` 一定可用。若数据库类型固定,可直接使用该数据库的当前时间表达式(例如 MySQL 的 `NOW()`);跨数据库代码则应按数据库类型选择表达式或传入平台已确认支持的时间值。
|
|
101
|
+
// ✅ 安全:使用 @p0, @p1 占位符,并用 AddInParameter 绑定
|
|
102
|
+
var list = V8.Db.FromSql(
|
|
103
|
+
'SELECT a.Id, a.Name, b.OrderCount FROM Customer a LEFT JOIN (SELECT CustomerId, COUNT(*) OrderCount FROM OrderHeader GROUP BY CustomerId) b ON a.Id = b.CustomerId WHERE a.Status = @p0'
|
|
104
|
+
).AddInParameter("@p0", 1).ToArray();
|
|
105
|
+
|
|
106
|
+
// ✅ 安全:多个参数
|
|
107
|
+
var row = V8.Db.FromSql(
|
|
108
|
+
'SELECT * FROM SysUser WHERE Account = @p0 AND DeptId = @p1'
|
|
109
|
+
).AddInParameter("@p0", V8.Param.account)
|
|
110
|
+
.AddInParameter("@p1", V8.Param.deptId)
|
|
111
|
+
.First();
|
|
112
|
+
|
|
113
|
+
// 统计
|
|
114
|
+
var count = V8.Db.FromSql(
|
|
115
|
+
'SELECT COUNT(*) FROM OrderHeader WHERE Status = @p0 AND CreateTime >= @p1'
|
|
116
|
+
).AddInParameter("@p0", 1)
|
|
117
|
+
.AddInParameter("@p1", V8.Param.startDate)
|
|
118
|
+
.ToScalar();
|
|
119
|
+
|
|
120
|
+
// 非查询(UPDATE / INSERT / DELETE)
|
|
121
|
+
V8.Db.FromSql(
|
|
122
|
+
'UPDATE SysUser SET LastLoginTime = @p0 WHERE Id = @p1'
|
|
123
|
+
).AddInParameter("@p0", DateNow('yyyy-MM-dd HH:mm:ss'))
|
|
124
|
+
.AddInParameter("@p1", V8.CurrentUser.Id)
|
|
125
|
+
.ExecuteNonQuery();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
> 老版本兼容:要把维护接口复制到旧部署时,不要假定 `DateNow` 或 `System.DateTime.Now.ToString(...)` 一定可用。若数据库类型固定,可直接使用该数据库的当前时间表达式(例如 MySQL 的 `NOW()`);跨数据库代码则应按数据库类型选择表达式或传入平台已确认支持的时间值。
|
|
129
129
|
|
|
130
130
|
### V8.Db 方法速查
|
|
131
131
|
|
|
@@ -140,34 +140,34 @@ V8.Db.FromSql(
|
|
|
140
140
|
|
|
141
141
|
### 读写分离
|
|
142
142
|
|
|
143
|
-
```javascript
|
|
144
|
-
V8.Db.FromSql(...) // 主库(读写)
|
|
145
|
-
V8.DbRead.FromSql(...) // 从库(只读,适合报表和大量查询)
|
|
143
|
+
```javascript
|
|
144
|
+
V8.Db.FromSql(...) // 主库(读写)
|
|
145
|
+
V8.DbRead.FromSql(...) // 从库(只读,适合报表和大量查询)
|
|
146
146
|
// 未部署读写分离时 V8.DbRead 与 V8.Db 一致
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
### 跨应用查询(扩展数据库)
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
var list = V8.Dbs.OracleDB1.FromSql('SELECT * FROM Table WHERE Id = @p0')
|
|
153
|
-
.AddInParameter("@p0", id)
|
|
154
|
-
.ToArray();
|
|
155
|
-
|
|
156
|
-
// 不写入 microi_database:创建仅当前请求使用的临时会话
|
|
157
|
-
var tempDb = V8.Dbs.Open(
|
|
158
|
-
'SqlServer',
|
|
159
|
-
'Server=127.0.0.1,1433;Database=app;User Id=user;Password=***;TrustServerCertificate=True;'
|
|
160
|
-
);
|
|
161
|
-
var tempList = tempDb.FromSql('SELECT Id, Name FROM Customer WHERE Status = @p0')
|
|
162
|
-
.AddInParameter('@p0', 1)
|
|
163
|
-
.ToArray();
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
`V8.Dbs.Open` 与保存连接均只支持 Dos.ORM 已认证的 `MySql`、`SqlServer`、`Oracle`、`PostgreSql`、`DaMeng`、`KingBase`。动态连接串只能来自可信服务端密钥或管理员代码,禁止使用 `V8.Param.ConnectionString`,禁止记录或返回。外部 SQL 的表名、列名、排序字段必须来自已校验元数据白名单;动态值继续使用 `AddInParameter`。
|
|
167
|
-
|
|
168
|
-
MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认使用只读的 `microi_query_external_database`。当用户明确要求数据库管理级能力时,使用独立的 `microi_execute_external_database`:它只允许后端确认的 `Level >= 9999` 当前用户调用,显式确认后可执行目标数据库账号有权执行的任意 DML、DDL、存储过程、数据库原生命令或多语句。输出行数限制只保护 MCP 传输,不限制数据库副作用;审计只记录 SQL 哈希、长度、模式和结果,不得记录 SQL 正文、连接串或密码。
|
|
169
|
-
|
|
170
|
-
不要用 `microi_get_db_schema` 读取第三方库,也不要在对话中搬运全库数据;持续同步应创建参数化、分页、幂等的服务端任务。最高权限入口不等于跨租户,也不能超越目标数据库账号自身权限。
|
|
149
|
+
### 跨应用查询(扩展数据库)
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
var list = V8.Dbs.OracleDB1.FromSql('SELECT * FROM Table WHERE Id = @p0')
|
|
153
|
+
.AddInParameter("@p0", id)
|
|
154
|
+
.ToArray();
|
|
155
|
+
|
|
156
|
+
// 不写入 microi_database:创建仅当前请求使用的临时会话
|
|
157
|
+
var tempDb = V8.Dbs.Open(
|
|
158
|
+
'SqlServer',
|
|
159
|
+
'Server=127.0.0.1,1433;Database=app;User Id=user;Password=***;TrustServerCertificate=True;'
|
|
160
|
+
);
|
|
161
|
+
var tempList = tempDb.FromSql('SELECT Id, Name FROM Customer WHERE Status = @p0')
|
|
162
|
+
.AddInParameter('@p0', 1)
|
|
163
|
+
.ToArray();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`V8.Dbs.Open` 与保存连接均只支持 Dos.ORM 已认证的 `MySql`、`SqlServer`、`Oracle`、`PostgreSql`、`DaMeng`、`KingBase`。动态连接串只能来自可信服务端密钥或管理员代码,禁止使用 `V8.Param.ConnectionString`,禁止记录或返回。外部 SQL 的表名、列名、排序字段必须来自已校验元数据白名单;动态值继续使用 `AddInParameter`。
|
|
167
|
+
|
|
168
|
+
MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认使用只读的 `microi_query_external_database`。当用户明确要求数据库管理级能力时,使用独立的 `microi_execute_external_database`:它只允许后端确认的 `Level >= 9999` 当前用户调用,显式确认后可执行目标数据库账号有权执行的任意 DML、DDL、存储过程、数据库原生命令或多语句。输出行数限制只保护 MCP 传输,不限制数据库副作用;审计只记录 SQL 哈希、长度、模式和结果,不得记录 SQL 正文、连接串或密码。
|
|
169
|
+
|
|
170
|
+
不要用 `microi_get_db_schema` 读取第三方库,也不要在对话中搬运全库数据;持续同步应创建参数化、分页、幂等的服务端任务。最高权限入口不等于跨租户,也不能超越目标数据库账号自身权限。
|
|
171
171
|
|
|
172
172
|
## 数据库事务
|
|
173
173
|
|
|
@@ -178,14 +178,14 @@ MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认
|
|
|
178
178
|
// 返回 Code=1 → 自动提交事务
|
|
179
179
|
// 返回 Code≠1 → 自动回滚事务
|
|
180
180
|
// 手动调用 V8.DbTrans.Commit() 或 V8.DbTrans.Rollback() 均无效
|
|
181
|
-
V8.Db.FromSql('UPDATE Account SET Balance = Balance - @p0 WHERE Id = @p1')
|
|
182
|
-
.AddInParameter("@p0", 100)
|
|
183
|
-
.AddInParameter("@p1", fromId)
|
|
184
|
-
.ExecuteNonQuery();
|
|
185
|
-
V8.Db.FromSql('UPDATE Account SET Balance = Balance + @p0 WHERE Id = @p1')
|
|
186
|
-
.AddInParameter("@p0", 100)
|
|
187
|
-
.AddInParameter("@p1", toId)
|
|
188
|
-
.ExecuteNonQuery();
|
|
181
|
+
V8.Db.FromSql('UPDATE Account SET Balance = Balance - @p0 WHERE Id = @p1')
|
|
182
|
+
.AddInParameter("@p0", 100)
|
|
183
|
+
.AddInParameter("@p1", fromId)
|
|
184
|
+
.ExecuteNonQuery();
|
|
185
|
+
V8.Db.FromSql('UPDATE Account SET Balance = Balance + @p0 WHERE Id = @p1')
|
|
186
|
+
.AddInParameter("@p0", 100)
|
|
187
|
+
.AddInParameter("@p1", toId)
|
|
188
|
+
.ExecuteNonQuery();
|
|
189
189
|
|
|
190
190
|
// V8.DbTrans 可传给 FormEngine 和 ApiEngine.Run 共享事务
|
|
191
191
|
V8.FormEngine.UptFormData('Table1', { Id: 'x', Status: 1 }, V8.DbTrans);
|
|
@@ -196,16 +196,16 @@ V8.ApiEngine.Run('other-engine', { Id: 'x' }, V8.DbTrans);
|
|
|
196
196
|
|
|
197
197
|
```javascript
|
|
198
198
|
// 扩展数据库需要手动管理事务
|
|
199
|
-
var exTrans = V8.Dbs.OracleDB1.BeginTransaction();
|
|
200
|
-
try {
|
|
201
|
-
exTrans.FromSql('UPDATE t1 SET a = @p0 WHERE Id = @p1')
|
|
202
|
-
.AddInParameter("@p0", 1)
|
|
203
|
-
.AddInParameter("@p1", id1)
|
|
204
|
-
.ExecuteNonQuery();
|
|
205
|
-
exTrans.FromSql('UPDATE t2 SET b = @p0 WHERE Id = @p1')
|
|
206
|
-
.AddInParameter("@p0", 2)
|
|
207
|
-
.AddInParameter("@p1", id2)
|
|
208
|
-
.ExecuteNonQuery();
|
|
199
|
+
var exTrans = V8.Dbs.OracleDB1.BeginTransaction();
|
|
200
|
+
try {
|
|
201
|
+
exTrans.FromSql('UPDATE t1 SET a = @p0 WHERE Id = @p1')
|
|
202
|
+
.AddInParameter("@p0", 1)
|
|
203
|
+
.AddInParameter("@p1", id1)
|
|
204
|
+
.ExecuteNonQuery();
|
|
205
|
+
exTrans.FromSql('UPDATE t2 SET b = @p0 WHERE Id = @p1')
|
|
206
|
+
.AddInParameter("@p0", 2)
|
|
207
|
+
.AddInParameter("@p1", id2)
|
|
208
|
+
.ExecuteNonQuery();
|
|
209
209
|
exTrans.Commit();
|
|
210
210
|
} catch (ex) {
|
|
211
211
|
exTrans.Rollback();
|
|
@@ -224,11 +224,11 @@ V8.Db.FromSql(sql).ToArray(); // SQL 注入漏洞!
|
|
|
224
224
|
// ❌ 禁止:动态拼接表名
|
|
225
225
|
var sql = "SELECT * FROM " + V8.Param.tableName + " WHERE Id = @p0";
|
|
226
226
|
|
|
227
|
-
// ✅ 正确做法:单表查询优先使用 FormEngine + _Where
|
|
228
|
-
var result = V8.FormEngine.GetTableData('SysUser', {
|
|
229
|
-
_Where: [['Account', '=', V8.Param.account]],
|
|
230
|
-
_PageSize: 20
|
|
231
|
-
});
|
|
227
|
+
// ✅ 正确做法:单表查询优先使用 FormEngine + _Where
|
|
228
|
+
var result = V8.FormEngine.GetTableData('SysUser', {
|
|
229
|
+
_Where: [['Account', '=', V8.Param.account]],
|
|
230
|
+
_PageSize: 20
|
|
231
|
+
});
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
## 常见查询模式
|
|
@@ -271,21 +271,21 @@ var result = V8.FormEngine.GetTableData('Customer', {
|
|
|
271
271
|
### 关联查询(SQL JOIN)
|
|
272
272
|
|
|
273
273
|
```javascript
|
|
274
|
-
var list = V8.Db.FromSql(`
|
|
275
|
-
SELECT o.Id, o.OrderNo, o.TotalAmount, c.Name AS CustomerName
|
|
276
|
-
FROM OrderHeader o
|
|
277
|
-
INNER JOIN Customer c ON o.CustomerId = c.Id
|
|
278
|
-
WHERE o.Status = @p0 AND o.CreateTime >= @p1
|
|
279
|
-
ORDER BY o.CreateTime DESC
|
|
280
|
-
`).AddInParameter("@p0", 1)
|
|
281
|
-
.AddInParameter("@p1", V8.Param.startDate)
|
|
282
|
-
.ToArray();
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## 注意事项
|
|
286
|
-
|
|
287
|
-
- `V8.Db.FromSql` 只传 SQL 字符串,参数占位符从 `@p0` 开始递增,动态值用 `.AddInParameter("@p0", value)` 绑定
|
|
288
|
-
- 服务端 `V8.FormEngine` 操作默认不触发表单 V8 事件;确需触发时在参数中加 `_InvokeType: 'Client'`
|
|
274
|
+
var list = V8.Db.FromSql(`
|
|
275
|
+
SELECT o.Id, o.OrderNo, o.TotalAmount, c.Name AS CustomerName
|
|
276
|
+
FROM OrderHeader o
|
|
277
|
+
INNER JOIN Customer c ON o.CustomerId = c.Id
|
|
278
|
+
WHERE o.Status = @p0 AND o.CreateTime >= @p1
|
|
279
|
+
ORDER BY o.CreateTime DESC
|
|
280
|
+
`).AddInParameter("@p0", 1)
|
|
281
|
+
.AddInParameter("@p1", V8.Param.startDate)
|
|
282
|
+
.ToArray();
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## 注意事项
|
|
286
|
+
|
|
287
|
+
- `V8.Db.FromSql` 只传 SQL 字符串,参数占位符从 `@p0` 开始递增,动态值用 `.AddInParameter("@p0", value)` 绑定
|
|
288
|
+
- 服务端 `V8.FormEngine` 操作默认不触发表单 V8 事件;确需触发时在参数中加 `_InvokeType: 'Client'`
|
|
289
289
|
- 查询结果数量较大时务必分页,`_PageSize` 默认最大 1000
|
|
290
290
|
- `V8.DbRead` 适用于不需要实时性的报表查询
|
|
291
291
|
- 接口引擎的事务由平台自动管理,**不要手动调用** `V8.DbTrans.Commit/Rollback`
|