Compare commits

..

No commits in common. "fac825245b8ab7142491d84105daf214407b1931" and "bd91c0fad12efebb4a2af914bc39a95f1ee07f0c" have entirely different histories.

2 changed files with 45 additions and 16 deletions

View File

@ -45,20 +45,16 @@ func (h *handler) SubmitAddressShare() core.HandlerFunc {
return return
} }
// 登录态验证 - 必须登录才能提交(确保地址归属正确) // 尝试获取登录用户信息 (可选)
var submitUserID *int64 var submitUserID *int64
authHeader := ctx.GetHeader("Authorization") authHeader := ctx.GetHeader("Authorization")
if authHeader == "" { if authHeader != "" {
ctx.AbortWithError(core.Error(http.StatusUnauthorized, 10027, "请先登录后再提交收货地址")) // 如果有 Authorization 尝试解析
return if claims, err := jwtoken.New(configs.Get().JWT.PatientSecret).Parse(authHeader); err == nil {
uid := int64(claims.SessionUserInfo.Id)
submitUserID = &uid
}
} }
claims, claimsErr := jwtoken.New(configs.Get().JWT.PatientSecret).Parse(authHeader)
if claimsErr != nil {
ctx.AbortWithError(core.Error(http.StatusUnauthorized, 10027, "登录已过期,请重新登录"))
return
}
uid := int64(claims.SessionUserInfo.Id)
submitUserID = &uid
ip := ctx.Request().RemoteAddr ip := ctx.Request().RemoteAddr
// 统一使用 ctx.RequestContext() 包含 context 内容 // 统一使用 ctx.RequestContext() 包含 context 内容

View File

@ -113,12 +113,12 @@ func (s *service) SubmitAddressShare(ctx context.Context, shareToken string, nam
s.logger.Info("SubmitAddressShare: Processing", zap.Int64("invID", claims.InventoryID), zap.Int64("owner", claims.OwnerUserID)) s.logger.Info("SubmitAddressShare: Processing", zap.Int64("invID", claims.InventoryID), zap.Int64("owner", claims.OwnerUserID))
// 1. 确定资产最终归属地 (实名转赠逻辑) // 1. 确定资产最终归属地 (实名转赠逻辑)
// 必须登录才能提交submittedByUserID 由 API 层保证非空 targetUserID := claims.OwnerUserID
if submittedByUserID == nil || *submittedByUserID <= 0 { isTransfer := false
return 0, fmt.Errorf("login_required") if submittedByUserID != nil && *submittedByUserID > 0 && *submittedByUserID != claims.OwnerUserID {
targetUserID = *submittedByUserID
isTransfer = true
} }
targetUserID := *submittedByUserID
isTransfer := targetUserID != claims.OwnerUserID
var addrID int64 var addrID int64
err = s.repo.GetDbW().Transaction(func(tx *gorm.DB) error { err = s.repo.GetDbW().Transaction(func(tx *gorm.DB) error {
@ -569,6 +569,15 @@ func (s *service) RedeemInventoryToPoints(ctx context.Context, userID int64, inv
return 0, err return 0, err
} }
// 校验转赠来源:通过转赠获得的资产不允许兑换积分(防薅积分漏洞)
transferCnt, _ := s.readDB.UserInventoryTransfers.WithContext(ctx).Where(
s.readDB.UserInventoryTransfers.InventoryID.Eq(inventoryID),
s.readDB.UserInventoryTransfers.ToUserID.Eq(userID),
).Count()
if transferCnt > 0 {
return 0, fmt.Errorf("transfer_inventory_cannot_redeem")
}
valueCents := inv.ValueCents valueCents := inv.ValueCents
valueSource := inv.ValueSource valueSource := inv.ValueSource
valueSnapshotAt := inv.ValueSnapshotAt valueSnapshotAt := inv.ValueSnapshotAt
@ -649,6 +658,30 @@ func (s *service) RedeemInventoriesToPoints(ctx context.Context, userID int64, i
return 0, fmt.Errorf("no_valid_inventory") return 0, fmt.Errorf("no_valid_inventory")
} }
// 3.5 排除通过转赠获得的资产(防薅积分漏洞)
invIDs := make([]int64, 0, len(invList))
for _, inv := range invList {
invIDs = append(invIDs, inv.ID)
}
transferredInvs, _ := s.readDB.UserInventoryTransfers.WithContext(ctx).
Where(s.readDB.UserInventoryTransfers.InventoryID.In(invIDs...)).
Where(s.readDB.UserInventoryTransfers.ToUserID.Eq(userID)).
Find()
transferredSet := make(map[int64]struct{}, len(transferredInvs))
for _, t := range transferredInvs {
transferredSet[t.InventoryID] = struct{}{}
}
filteredInvList := make([]*model.UserInventory, 0, len(invList))
for _, inv := range invList {
if _, isTransferred := transferredSet[inv.ID]; !isTransferred {
filteredInvList = append(filteredInvList, inv)
}
}
if len(filteredInvList) == 0 {
return 0, fmt.Errorf("transfer_inventory_cannot_redeem")
}
invList = filteredInvList
// 4. 按资产快照计算总积分,缺失快照时回退商品价格并回写 // 4. 按资产快照计算总积分,缺失快照时回退商品价格并回写
productIDs := make([]int64, 0, len(invList)) productIDs := make([]int64, 0, len(invList))
productIDSet := make(map[int64]struct{}) productIDSet := make(map[int64]struct{})