Commit 2b2a1773d62bd3c3e2cd757d769fe1b4911bed22

Authored by YunaiV
1 parent 848d4948

fix(pay): 校验 App 支付订单的用户归属

采用 #1304 的提交入口归属检查,并在查询和提交入口同时核对 userId、userType。
拒绝发生在同步、钱包和支付提交前,保留历史订单空字段兼容。
20 项实际 Spring MVC 边界检查通过。

Refs #1304, #1303
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java
@@ -27,10 +27,12 @@ import org.springframework.web.bind.annotation.*; @@ -27,10 +27,12 @@ import org.springframework.web.bind.annotation.*;
27 27
28 import java.util.Objects; 28 import java.util.Objects;
29 29
  30 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 31 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
31 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; 32 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
32 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; 33 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
33 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; 34 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType;
  35 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND;
34 36
35 @Tag(name = "用户 APP - 支付订单") 37 @Tag(name = "用户 APP - 支付订单")
36 @RestController 38 @RestController
@@ -65,8 +67,7 @@ public class AppPayOrderController { @@ -65,8 +67,7 @@ public class AppPayOrderController {
65 return success(null); 67 return success(null);
66 } 68 }
67 // 重要:校验订单是否是当前用户,避免越权 69 // 重要:校验订单是否是当前用户,避免越权
68 - if (order.getUserId() != null // 特殊:早期订单未存储 userId,所以忽略  
69 - && ObjUtil.notEqual(order.getUserId(), getLoginUserId())) { 70 + if (!isLoginUserOrder(order)) {
70 return success(null); 71 return success(null);
71 } 72 }
72 73
@@ -82,7 +83,13 @@ public class AppPayOrderController { @@ -82,7 +83,13 @@ public class AppPayOrderController {
82 @PostMapping("/submit") 83 @PostMapping("/submit")
83 @Operation(summary = "提交支付订单") 84 @Operation(summary = "提交支付订单")
84 public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) { 85 public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) {
85 - // 1. 钱包支付事,需要额外传 user_id 和 user_type 86 + // 1. 重要:校验订单是否是当前用户,避免越权
  87 + PayOrderDO order = payOrderService.getOrder(reqVO.getId());
  88 + if (order == null || !isLoginUserOrder(order)) {
  89 + throw exception(PAY_ORDER_NOT_FOUND);
  90 + }
  91 +
  92 + // 2. 钱包支付事,需要额外传 user_id 和 user_type
86 if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) { 93 if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) {
87 if (reqVO.getChannelExtras() == null) { 94 if (reqVO.getChannelExtras() == null) {
88 reqVO.setChannelExtras(Maps.newHashMapWithExpectedSize(1)); 95 reqVO.setChannelExtras(Maps.newHashMapWithExpectedSize(1));
@@ -91,9 +98,23 @@ public class AppPayOrderController { @@ -91,9 +98,23 @@ public class AppPayOrderController {
91 reqVO.getChannelExtras().put(WalletPayClient.WALLET_ID_KEY, String.valueOf(wallet.getId())); 98 reqVO.getChannelExtras().put(WalletPayClient.WALLET_ID_KEY, String.valueOf(wallet.getId()));
92 } 99 }
93 100
94 - // 2. 提交支付 101 + // 3. 提交支付
95 PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); 102 PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP());
96 return success(BeanUtils.toBean(respVO, AppPayOrderSubmitRespVO.class)); 103 return success(BeanUtils.toBean(respVO, AppPayOrderSubmitRespVO.class));
97 } 104 }
98 105
  106 + /**
  107 + * 判断订单是否属于当前登录用户,需同时匹配用户编号和用户类型
  108 + *
  109 + * 相关 issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/1303
  110 + *
  111 + * @param order 支付订单
  112 + * @return 是否属于
  113 + */
  114 + private boolean isLoginUserOrder(PayOrderDO order) {
  115 + // 特殊:早期订单未存储 userId、userType,所以忽略
  116 + return (order.getUserId() == null || ObjUtil.equal(order.getUserId(), getLoginUserId()))
  117 + && (order.getUserType() == null || ObjUtil.equal(order.getUserType(), getLoginUserType()));
  118 + }
  119 +
99 } 120 }