13 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 7234 | 车务出行人明文与供应商域判权口径统一到 token 当前角色(契约不变,仅拒绝口径与审计语义) | admin | jw(GIT) | 修改接口 | deployed | verified | not_required | mmg | 2026-09-07 | 前端 mmg 已 grep 实查为 not_required:契约(请求/响应/错误码)全不变,仅后端判权依据由库默认角色改为 token 当前角色(X-Admin-Role),纯服务端加固。车务明文接口前端经 src/api/fleet/board.js:37 调用但契约不变;供应商 items/list 前端无该路径直接调用;前端 hasPermission(directives/permission.js、stores/user.js)是读自身 store 权限码做 UI 显隐,不感知后端判权口径;X-Admin-Role/roleHasPermission/PERMISSION_CHECK_FAILED 前端零读取。撤权 10 分钟生效为后端缓存 TTL,无需前端改动。 | 2026-09-07 | dev-v3 |
车务/供应商: 敏感读取判权统一到 token 当前角色
服务: hl-fleet-service (8086/8186) · hl-resource-service · hl-order-service-v3 · hl-user-service PR: #7272 Issue: #7234 日期: 2026-09-07 影响范围: 管理后台「车务派单看板·查看出行人明文」与「供应商域读写」两条链路的鉴权判定口径
⚠️ 关键变化
请求、响应、错误码全部不变,前端不改代码。 唯一变化是「谁有权限」的判定依据:
由数据库默认角色(admin_user.current_role_id)改为本次请求 token 的当前角色(网关注入的 X-Admin-Role)。
对正常账号无可见变化——正常登录时 token 角色与库角色一致。差异只在两者分叉时出现:
短信登录会把 token 角色临时置成 CUSTOMIZER 却不改库里的默认角色,改后这类会话一律按 CUSTOMIZER 判权,不再借用库角色的权限码。
撤权最迟 10 分钟生效:角色权限码走 Redis 缓存(admin_permissions:v2:role:{roleKey},TTL 10 分钟)。撤掉某角色的权限码关联后,最多 10 分钟后该角色被拒。
一、背景
出行人明文(姓名/证件号/手机号/紧急联系人)是平台敏感度最高的数据。链路上已有调用方白名单、token 与 adminId 一致性校验、二次权限校验、访问审计四道闸,但权限那道判的是库角色:一旦 token 角色与库角色分叉,闸门放行与否取决于一个与本次会话无关的字段。
这不是「当前公开入口已可越权读明文」——车务入口在网关层已被 VEHICLE_MANAGER/SUPER_ADMIN 门禁挡住,供应商守卫也先校验 token 角色集合。本单是口径加固:把「谁在操作」的唯一事实来源固定为 token 角色,消除库角色与 token 角色分叉时静默放权的可能,并补上 order-v3 内部二次校验缺失的角色绑定(不接受调用方自报角色)。与 #7210 复审返工同一口径。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 车务查看出行人明文 | POST | /admin/fleet/board/orders/:orderId/travelers/plain |
修改接口 | 契约不变;无权限判定由库角色改按 token 当前角色 |
| 2 | 供应商列表(供应商域读写代表) | GET | /admin/supplier/items/list |
修改接口 | 契约不变;供应商域读/写/授信/状态/资源关系全部由库角色改按 token 当前角色,角色集合前置校验不变 |
三、接口详情
1. 车务查看出行人明文 POST /admin/fleet/board/orders/:orderId/travelers/plain
VO: 无请求体 → Result<List<OrderTravelerPlainForFleetDTO>>
使用场景
车务派单看板「查看出行人明文」。持 VEHICLE_MANAGER/SUPER_ADMIN 且其角色拥有 FLEET_TRAVELER_PLAIN_VIEW 权限码的车务管理员,查看某订单出行人的明文姓名/证件号/手机号/紧急联系人。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | path | Long | 是 | 雪花 ID | 订单 ID |
| Authorization | header | String | 是 | Bearer <管理端 token> |
网关透传 |
出参 Result<List<OrderTravelerPlainForFleetDTO>>
| 字段 | 类型 | 说明 |
|---|---|---|
| name | String | 明文姓名(不变) |
| idNo | String | 明文证件号(不变) |
| phone | String | 明文手机号(不变) |
| emergencyContact | String | 紧急联系人(不变) |
| emergencyPhone | String | 紧急联系人电话(不变) |
| ageAtDeparture | Integer | 按订单出发日计算的年龄(不变) |
本单不改返回结构,字段与改前完全一致。
请求示例
POST /admin/fleet/board/orders/2094610801951203329/travelers/plain
Authorization: Bearer <VEHICLE_MANAGER 的 token>
响应示例
{
"code": 200,
"success": true,
"data": [
{"travelerId": "...", "name": "张三", "idNo": "...", "phone": "...", "emergencyContact": "...", "ageAtDeparture": 10}
]
}
成功时 fleet_sensitive_access_log 落一条 SUCCESS(row_count = 返回条数)。
空数据 / 降级响应
该订单无出行人时返回空数组。权限服务不可用一律 fail-closed(不返回明文)。
错误响应
{"code": 605039, "success": false, "message": "无权查看出行人明文信息", "data": null}
| 码 | 触发 | 审计落库 |
|---|---|---|
| 403(网关) | 当前角色非 VEHICLE_MANAGER/SUPER_ADMIN |
请求未到达 fleet,不写审计 |
| 605039 | 到达 fleet 后:当前角色无 FLEET_TRAVELER_PLAIN_VIEW 权限码 |
DENIED + PERMISSION_DENIED |
| 605039 | 权限服务不可用(Feign 降级) | DENIED + PERMISSION_CHECK_FAILED(与上一行区分,供排查故障 vs 无权限) |
业务边界
- 判权口径变化:无权限判定由
hasPermission(adminId, FLEET_TRAVELER_PLAIN_VIEW)(库角色)改为roleHasPermission(当前角色, FLEET_TRAVELER_PLAIN_VIEW)(token 角色)。 - 审计语义不变:拒绝仍写
DENIED+PERMISSION_DENIED;FAILED仍只用于 order 服务调用失败分支。新增区分:权限服务降级写DENIED+PERMISSION_CHECK_FAILED。 - 内部边界加固:order-v3 侧二次校验改为从被校验的 token 上取角色判权,请求体声明的角色只做一致性比对,不一致即拒——不接受调用方自报角色。
2. 供应商列表(供应商域读写代表) GET /admin/supplier/items/list
VO: SupplierListQueryReqVO → Result<List<SupplierListItemRespVO>>
使用场景
供应商管理列表查询。此处作为供应商域的代表:域内读/写/授信写/状态写/资源关系维护端点判权口径同步变化,请求/响应/错误码均不变。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| (查询条件) | query | — | 否 | 同改前 | 本单不改查询参数 |
| Authorization | header | String | 是 | Bearer <管理端 token> |
网关透传 |
出参 Result<List<SupplierListItemRespVO>>
| 字段 | 类型 | 说明 |
|---|---|---|
| supplierId | String | 供应商 ID(雪花,序列化为字符串,不变) |
| supplierNo | String | 供应商编号(不变) |
| shortName | String | 供应商简称(不变) |
本单不改返回结构,字段与改前完全一致。
请求示例
GET /admin/supplier/items/list
Authorization: Bearer <ADMIN/FINANCE/SUPER_ADMIN 的 token>
响应示例
{"code": 200, "success": true, "data": [ { "supplierId": "...", "shortName": "..." } ]}
空数据 / 降级响应
无命中返回空数组。权限服务不可用一律按无权限 fail-closed(拒绝)。
错误响应
{"code": 403, "success": false, "message": "无权访问供应商数据", "data": null}
| 码 | 触发 |
|---|---|
| 403 | 当前 token 角色不在读集合(READ_ROLES=ADMIN/FINANCE/SUPER_ADMIN)内 |
| 403 | 角色在集合内但该角色无对应权限码(读);写端点按其既有 395002/状态码拒 |
业务边界
- 判权口径变化:域内私有判权由
hasPermission(adminId, code)(库角色)改为roleHasPermission(role, code)(token 角色)。 - 角色集合前置校验不变:
READ_ROLES/WRITE_ROLES/STATUS_WRITE_ROLES/RESOURCE_RELATION_*仍先按 token 角色集合挡一道,对正常账号无可见变化。
四、契约约束与正确调用方式
✅ 正确 / ❌ 错误理解
- ✅ 判权只认本次请求 token 的当前角色(
X-Admin-Role)。多角色账号必须先切到有权限的角色再操作。 - ❌ 不要以为「库里默认角色有权限就一定能读」——短信登录等临时切角色的会话按 token 角色判,不借库角色的码。
- ✅ 撤销某角色的权限码后,最多 10 分钟生效(角色权限码缓存 TTL)。
五、数据库行为
- 车务明文入口:只对
fleet_sensitive_access_log追加一条审计(成功SUCCESS/拒绝DENIED/失败FAILED),无其他业务表写入。判权读的是 user-service 的角色权限码缓存(Redis,TTL 10 分钟),非本服务库表。 - 供应商域读端点:无写库;判权同样读 user-service 角色权限码。
- 全链路无表结构变更、无 Flyway。
六、边界行为
- 车务明文入口:网关角色门禁(403,不写审计)在前,服务内权限码判定(605039,写审计)在后,口径都是 token 角色。
- 供应商域:角色集合前置校验(403)在前,权限码判定在后,口径都是 token 角色。
- 权限服务不可用:车务侧写
DENIED+PERMISSION_CHECK_FAILED(区分于无权限);供应商侧按无权限拒(其域无区分故障/无权限的审计语义)。
六.6、修改前后对比
| 维度 | 改前 | 改后 |
|---|---|---|
| 车务明文判权依据 | 库默认角色 admin_user.current_role_id(hasPermission(adminId, code)) |
本次 token 当前角色 X-Admin-Role(roleHasPermission(role, code)) |
| 供应商域判权依据 | 同上,按 adminId 落库角色 | 按 token 当前角色 |
| order-v3 内部二次校验 | 只绑定 adminId,角色可由调用方自报 | 角色从被校验 token 上取,请求体声明只做一致性比对,不一致即拒 |
| 权限服务不可用时车务审计 | DENIED+PERMISSION_DENIED |
新增区分:DENIED+PERMISSION_CHECK_FAILED |
| 请求 / 响应 / 错误码 | — | 完全不变 |
六.7、影响评估
- 前端:无需改代码。正常账号(token 角色=库角色)行为不变;只需知悉拒绝语义与「撤权最迟 10 分钟生效」。
- 正常账号:无可见变化。
- 临时切角色会话(如短信登录临时
CUSTOMIZER):按 token 角色判权,不再借用库角色权限码——这是本单的目的。 - 回滚:纯判权口径与内部端点变更,无数据迁移,回滚 PR 即可;但需按部署顺序(user-service 先上)。
七、不影响范围
- 请求参数、响应结构、错误码:全部不变。
- 网关路由与
/admin/fleet/**角色门禁:不变。 - 脱敏返回结构、敏感访问审计落库链路:不变(只读,不改写入语义)。
- Flyway / 表结构:无变更。
- 服务间内部端点(
/internal/user/admin/access-token-context新增替代validate-access-token)属服务间调用,不面向前端。
八、测试环境已验证
TEST 网关 + 真实鉴权实测(2026-09-07,PR #7272 分支部署 user→order-v3→fleet→resource):
车务明文(AC-10/AC-11)
- 临时
CUSTOMIZERtoken 调明文入口 → 网关403,fleet_sensitive_access_log计数不变(未到 fleet)。 - 真实
VEHICLE_MANAGER(持FLEET_TRAVELER_PLAIN_VIEW)登录 token 调同入口 → 返回明文(3 条),审计落SUCCESS(row_count=3)。 - 撤掉
VEHICLE_MANAGER的FLEET_TRAVELER_PLAIN_VIEW关联并等角色缓存过期后 → 同账号同角色被拒605039,审计落DENIED+PERMISSION_DENIED;恢复关联后放行。
供应商域(AC-12)
- 同一 adminId(库角色
VEHICLE_MANAGER,无supplier:list),token 角色ADMIN/FINANCE→ 200 出数,CUSTOMIZER/VEHICLE_MANAGER→ 403。判权按 token 角色而非库角色(否则四种都应被拒)。
单测与门禁
- user/order-v3/fleet/resource 本单新增改动单测全绿;fleet
spotless:check干净。含 AC-4 自报角色绕过用例、AC-5 实经 fallback 工厂用例。
十、相关文档
- 工单 #7234;同口径前序 #7210 复审返工。
关联 / 联系人
链接
联系人
- 后端: jw
- 前端: mmg(无需改代码;知悉拒绝语义与撤权 10 分钟生效即可)