exchange-code-security.md 4.2 KB

兑换码入口防护

本次变更

  • 后台 /manage/exchange/code/** 增加类级后台登录校验;保留各接口权限。
  • 为兼容当前前端,保留 GET /createAndExport 路径,但必须同时具有 exchangeCode:add 和 exchangeCode:export。后续前端联动再迁移为 POST。
  • 生成入口在服务层限制为每次 1–500 个码、1–100 个规格,并检查类型枚举。
  • 后台查询/导出只接收 goodsId、skuId、code、userId、useStatus、 codeType、start、limit;不再接收客户端 condition、操作符、逻辑分组及动态排序。 code 改为精确匹配。分页大小最多 100;导出最多 10000 条,且只导出平台类型码。
  • root 判定改为精确角色匹配;非 root 仍按历史 operator 昵称限定范围, 空昵称或重名则拒绝查询。历史表没有管理员 ID 归属,本次不猜测或回填归属; 昵称变更后仍需管理员核实历史归属,后续应迁移到 operator_id。
  • /external/app/get/mirror 和 /external/app/refund/{id} 仅允许后台 root, 分别要求 exchangeCode:view、exchangeCode:refund。禁止匿名第三方调用; 暂不支持第三方独立凭证或租户范围,接入方需迁移,不能恢复匿名访问。
  • 外部查询不再透传请求参数;退款增加审计,并记录重发码操作者。
  • 前台按兑换码查看详情只接受非空完整码,忽略客户端查询操作符。
  • 未删除、修改任何生产兑换码;未改 GPT token 接口。

发布

运行 docs/sql/20261021-coupon-exchange-permissions.sql 补齐 9 个权限码, 包含新增的 exchangeCode:refund。只自动授予 root,其他角色显式授权。 后台重新保存角色权限刷新缓存,重新登录验证。代码必须构建、发布才会生效。

验收(尚未在线执行)

  1. 匿名/仅前台 token 调用后台和 external 接口:拒绝,数据无变化。
  2. 后台无相应权限:拒绝;只有 add 没有 export 不能调用 createAndExport。
  3. external:普通后台角色即使有 view/refund 仍被拒绝,root 且有权限才允许。
  4. 查询携带 condition、code-op、operator-op 等额外参数:不得改变结果范围。
  5. 非 root 看不到其他唯一昵称创建者的码;重名/空昵称拒绝查询。
  6. 数量 0、负数、501、空规格、未知类型均拒绝;合法请求可正常生成。
  7. 分页超过 100 拒绝;导出超过 10000 条拒绝。
  8. 前台详情缺少 code 或只有查询操作符不能返回兑换码。

回归测试命令(先修复现有腾讯云依赖缺失):

& 'D:\IntelliJ IDEA 2025.2.3\plugins\maven\lib\maven3\bin\mvn.cmd' -pl netflix-web -am test '-Dtest=ExchangeCodeSecurityTest,CouponWriteSecurityTest' '-Dsurefire.failIfNoSpecifiedTests=false'

2026-10-21 用户端兑换码详情加固

  • GET /applets/user/get/ticket/code/detail 只接受完整 code,缺失、空串、空白、非法字符和超过 256 字符均拒绝。兼容字母数字、下划线和连字符。
  • 使用框架一次 URL 解码后的值,不再二次解码。改用 MyBatis 参数绑定的 code 等值查询,额外 skuIds/useStatus/排序/操作符不参与查询。
  • 返回前用 Java 严格比较原码,避免数据库不区分大小写的匹配;响应不再包含 code 字段。前端应使用自己的输入码执行兑换,不依赖详情响应回填。
  • 不修改兑换核销流程,不增加数据库迁移。此改动未新增限流,不代表全部兑换安全问题已完成。
  • 部署验收:空码与错误码不返回数据;完整有效测试码正常展示;附加参数不能改变查询目标;响应不含完整兑换码。

2026-10-21 外部镜像查询访问策略调整

按用户要求,GET /external/app/get/mirror 恢复公开查询,移除该查询的后台登录/root/兑换码查询权限限制。固定规格、参数白名单和分页上限保留。 PUT /external/app/refund/{id} 仍要求后台登录、root 和 exchangeCode:refund;类级权限移至退款方法,未取消退款授权。 此为公开访问策略,不代表返回的完整镜像兑换码属于保密数据;未修改用户端兑换码详情的精确码校验。