9

我正在建立之前与 Jon Skeet 的讨论

我的方案的要点如下:

  • 客户端应用程序能够创建新的“PlaylistItem”对象,这些对象需要保存在数据库中。
  • 用例要求以这样一种方式创建 PlaylistItem,即客户端在显示 PlaylistItem 之前不必等待来自服务器的响应。
  • 客户端为 PlaylistItem 生成一个 UUID,在客户端显示 PlaylistItem,然后向服务器发出保存命令。

在这一点上,我明白在我的数据库中使用客户端生成的 UUID 作为对象的 PK 是不好的做法。原因是恶意用户可以修改生成的 UUID 并在我的数据库上强制 PK 冲突。

为了减轻因在 PlaylistItem 上强制发生 PK 冲突而造成的任何损害,我选择将 PK 定义为两个 ID 的组合 - 客户端生成的 UUID 和服务器生成的 GUID。服务器生成的 GUID 是 PlaylistItem 的播放列表的 ID。

现在,我已经使用这个解决方案一段时间了,但我不明白为什么/相信我的解决方案比简单地信任客户端 ID 更好。如果用户能够强制与另一个用户的 PlaylistItem 对象发生 PK 冲突,那么我认为我应该假设他们也可以提供该用户的 PlaylistId。他们仍然可以强制碰撞。

是的。做这样的事情的正确方法是什么?允许客户端创建一个 UUID,成功保存后服务器会竖起大拇指。如果发现冲突,恢复客户端更改并通知检测到冲突?

4

2 回答 2

4

可以信任客户端生成的 UUID 或服务器上类似的全局唯一标识符。明智地去做。

您的大多数表/集合还将保存一个用户 ID,或者能够通过 FK 将自己与用户 ID 关联。

如果您正在执行插入并且恶意用户使用现有密钥,则插入将失败,因为记录/文档已经存在。

如果您正在进行更新,那么您应该验证登录用户是否拥有该记录或被授权(例如管理员用户)来更新它。如果强制执行纯所有权(即没有管理员用户场景),那么您where查找记录/文档的条款将包括 Id 和 userId。现在从技术上讲,userId 在 where 子句中是多余的,因为 Id 将唯一地找到一个记录/文档。但是,添加 userId 可以确保记录属于进行更新的用户而不是恶意用户。

我假设服务器正在解密某种加密令牌或会话以确定 userId,并且这不是由客户端提供的,否则显然不安全。

于 2016-08-28T02:57:57.050 回答
3

一个不错的解决方案如下:引用 Sam Newman 的“构建微服务”

调用系统会发布一个 BatchRequest,可能会传入一个可以放置所有数据的文件的位置。客户服务将返回 HTTP 202 响应代码,指示请求已被接受,但尚未处理。然后调用系统可以轮询资源,等待它检索到 201 Created 指示请求已完成

因此,在您的情况下,您可以 POST 到服务器,但会立即收到类似“我将保存 PlaylistItem 并且我保证它的 Id 将是这个”的响应。然后,客户端(和用户)可以继续,而服务器(甚至可能不是 API,而是一些从 API 获取消息的后台处理器)需要时间来处理、验证和执行其他可能很繁重的逻辑,直到它保存实体。如前所述,API 可以为该请求的状态提供一个 GET 端点,客户端可以轮询它并在出现错误时采取相应的行动。

于 2015-02-27T11:43:22.160 回答