You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Node.js的桌面RPG角色表XP/资源消耗服务端验证咨询

服务端校验核心实现方案

前端校验仅用于优化用户体验,所有可信校验必须在服务端完成,不要信任前端传递的任何敏感数值。

  • 敏感数据全量存储在服务端,不要同步完整规则到前端
    用户的XP余额、所有能力的阶梯定价表全量存在服务端的数据库或官方配置文件中,前端仅展示当前用户的XP余额、单个能力当前等级对应的下一级消耗,永远不要把完整定价表、XP计算逻辑下发到前端。用户发起兑换请求时,前端仅需要传递能力ID、目标兑换等级即可,不需要传递消耗XP数值、当前余额等敏感参数。

  • 兑换请求全流程校验逻辑

  1. 先完成身份校验:通过session/Token确认请求对应的用户ID,避免跨用户伪造操作
  2. 从服务端数据库拉取对应用户的当前XP余额、该能力已持有等级,完全不采信前端传递的相关数值
  3. 拉取服务端存储的官方阶梯定价表,根据用户当前持有等级、目标等级,自行计算本次兑换需要消耗的总XP,完全不采信前端传递的消耗数值
  4. 比对用户当前XP >= 总消耗:如果余额不足直接返回错误,拒绝操作
  5. 校验通过后执行原子操作:先扣除对应XP,再更新用户该能力的持有等级,最后才返回操作成功的结果
  6. 操作完成后主动将最新的XP余额、能力等级同步给前端做展示更新即可
  • 额外防篡改优化
  • 全量留操作日志:每个用户的XP获取记录、消耗记录、能力变更记录都永久存在服务端,可随时回溯对账,可定期校验「用户当前XP = 累计获得XP - 累计消耗XP」的恒等式,避免数值异常
  • 所有涉及XP计算、定价计算的逻辑统一封装为服务端公共函数,所有相关接口统一调用,避免不同逻辑分支规则不一致出现漏洞
  • 涉及数据变更的操作优先用数据库原子操作实现,避免并发请求导致的数值错乱

Node.js 简易实现示例(Express框架)

// 仅存储在服务端的官方定价配置,永远不下发给前端
const abilityPriceConfig = {
  // 能力ID对应阶梯价格数组,索引为等级,值为升到该级需要的XP
  "strength": [0, 10, 20, 40, 80],
  "magic": [0, 15, 30, 60, 120]
}

// 兑换能力接口
app.post('/api/ability/exchange', async (req, res) => {
  const { abilityId, targetLevel } = req.body
  const userId = req.auth.userId // 从鉴权中间件获取当前登录用户ID

  // 校验参数合法性
  if (!abilityPriceConfig[abilityId] || targetLevel < 1 || targetLevel > abilityPriceConfig[abilityId].length - 1) {
    return res.status(400).json({ msg: "无效的能力或目标等级" })
  }

  // 从服务端拉取用户当前数据
  const user = await UserModel.findById(userId)
  const currentLevel = user.abilities[abilityId] || 0
  if (targetLevel <= currentLevel) {
    return res.status(400).json({ msg: "目标等级不能低于当前已持有等级" })
  }

  // 服务端自行计算总消耗
  let totalCost = 0
  for (let i = currentLevel + 1; i <= targetLevel; i++) {
    totalCost += abilityPriceConfig[abilityId][i]
  }

  // 校验余额
  if (user.xp < totalCost) {
    return res.status(400).json({ msg: "XP余额不足" })
  }

  // 原子更新用户数据
  await UserModel.updateOne(
    { _id: userId },
    { 
      $inc: { xp: -totalCost },
      $set: { [`abilities.${abilityId}`]: targetLevel }
    }
  )

  // 返回最新数据给前端更新展示
  const updatedUser = await UserModel.findById(userId, { xp: 1, abilities: 1 })
  res.json({ success: true, data: updatedUser })
})

内容的提问来源于stack exchange,提问作者Bob McCroy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 22:06:03