文件
hl-api-changelog/changelogs-v2/2026-09/07_7234_敏感读取判权统一到token当前角色-修改接口-管理后台.md
T
2026-09-07 16:37:29 +08:00

13 KiB
原始文件 Blame 文件历史

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)

  • 临时 CUSTOMIZER token 调明文入口 → 网关 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 分钟生效即可)