【问题标题】:Best method to verify multi-level relational dependencies验证多级关系依赖的最佳方法
【发布时间】:2011-07-21 22:28:26
【问题描述】:

假设您有实体 A、B、C 和 D。

  • D 与 C 相关
  • C 与 B 相关
  • B 与 A 相关

此外,如果用户拥有 A,则用户只能在 D 上操作

在应用程序的某个状态下,您包含指向访问 D 的页面的链接。因此,您将 D 的 ID 作为 GET 或 POST 参数包含在内。

如果用户点击链接,应用程序会检索 D 的 ID 并开始对 D 进行操作。

简单的应用程序使用这样的 URL [modulo URL-rewriting]:

http://www.myServer.com/?action=1234&entity=D&ID=23

如何验证是否允许用户对D进行操作?

A) 显而易见的解决方案是:给定 D,找到 C,然后找到 B,最终找到 A。 如果链在某处断裂,则访问 D 将被拒绝。不幸的是,这需要 - 如果简单地实现 - 4 次数据库访问,而不仅仅是 A 的一次。

B) 另一种解决方案是将当前会话中的 D 的 ID 保存在一组可访问的实体中,以供下一个要呈现的页面使用。 p>

C) 作为替代方案,可以以某种方式加密 GET 和 POST 参数。在每个页面请求上,第一个操作是解密请求的参数。如果解密操作失败,访问将被拒绝。

D) 或者,在 infinitum 中,对所有页面中的所有链接进行哈希处理,在会话中保留一个映射,将哈希值与 URL 关联起来,并且只将哈希值写入网页.

E) 最后,您可以在 D 中保留对 A、B 和 C 的引用,在 C 中保留对 A 和 B 的引用,在 B 中保留对 A 的引用。因此,在每个级别,都有一个能够立即找到根实体。

在这种情况下,您的解决方案是什么?为什么?

虽然我包含了 PHP 标记,但我不想将这个问题集中在一种语言上。我很乐意得到一般性的建议。或已在例如已实施的解决方案ORM 层。

UPDATE-1

最后,我选择了D)

一般原则:

确保以某种方式从属实体的 ID 始终以安全/可信的方式传递。这样一来,第三方就无法改变他们的价值观。

详情:

此选项在设计上提供了许多好处:

首先,链接页面的 ID 或其他参数永远不会到达浏览器。而不是

http://www.myServer.com/?action=1234&entity=D&ID=23

大多数页面都是这样链接的

http://www.myServer.com/?forwardHash=78sd7sdf98asd7ad5aa76asa4a465

要执行的下一页的所有参数完全保存在用户会话中

由于页面的所有参数都保存在用户会话中,需要的检查要少得多。特别是,上面提到的关系依赖检查不再使用。如果用户会话中有某些内容,则它是从先前受信任的对话步骤中放入的。

此外,甚至可以强制用户仅调用当前呈现页面上可用的链接。每次他们调用链接时,应用程序都可能使页面的所有其他链接失效。因此,用户将无法在多个窗口中打开页面并认为他们看到了应用程序的两种不同“状态”。如果他们两次调用链接,应用程序可能会显示错误消息。

最后,可以直接建立我称之为子工作流对话框的东西:您可以通过将当前页面的 URL 推送到 会话中的延续堆栈来启动对话框> 并打开编辑对话框步骤。用户可以有序地结束或有意取消对话工作流程。如果继续堆栈不为空,取消工作流链接可能会自动显示为用户选项。

通过将延续保留在会话中的堆栈上,它与当前正在运行的对话步骤完全隔离。对话步骤甚至对它的调用者一无所知。

通过将功能包装在一个小的管理器调用中,子流程最终调用 FlowManager::finishFlow()。此调用从堆栈中弹出一个延续并将浏览器重定向到此页面 - 有效地返回到工作流开始的位置

由于我们使用了一堆延续,甚至可以运行从属于其他子工作流的子工作流

【问题讨论】:

  • 我说的对吗,A/B/C/D 本质上是分层数据,您可以查看Bill Karwin's options for hierarchival data,找到一个可以让您获得整个树的数据(=rights)在一个查询中?
  • 是的。人们可以将这种情况视为分层结构化数据。我去看看。
  • 是不是说在被编辑的实体和用户拥有的实体之间的链中可能存在任意数量的链接 - 它不限于您示例中的三个链接?跨度>
  • 它是固定的,但可能会有所不同:A 到 C,或 A 到 D。我不是在寻找可能无限链接链的反身外壳。
  • 用户是否必须拥有链中的每个实体,还是仅拥有链中的最终实体?此外,如果一个实体从链中移除,链应该中断还是保持有效?例如,给定D > C > B > A,并且用户拥有A,如果删除B,用户是否仍然拥有A - 即,链是否变为D > C > A

标签: php mysql relational-database


【解决方案1】:

显而易见的解决方案是:给定 D,找到 C,然后找到 B,然后 最终找到 A。如果链在某处断裂,则访问 D 将是 被拒绝。不幸的是,这需要 - 如果简单地实现 - 4 数据库访问,而不仅仅是 A 的访问。

我想这是可能的。它部分取决于“相关”的含义,但假设架构相对简单,我希望您能够在单个 SQL 语句中连接所有四个表。如果缺少部分链,则查询将不返回任何行。

还是我错过了什么?

【讨论】:

  • 不,没什么。但是除了验证 D 以某种方式属于用户之外,我不想强​​迫 D 的控制器对他们的环境了解太多。理想情况下,D 的控制器不应该了解 D 与其他实体的关系。
  • 如果您限制客户端访问存储过程,D 的控制器将不知道实体之间的关系。 (假设“D 的控制器”指的是客户端代码。)他们只会知道您返回的数据。 (可能还有列名,尽管您可以在 SP 中为它们设置别名。)
【解决方案2】:

我不确定我是否理解您想要实现的目标,但您不能使用选项 A 来验证是否允许用户仅使用 一个 在 D 上进行操作> 访问数据库?:

SELECT D.*
  FROM D
    JOIN C 
      ON C.id = D.cid
    JOIN B
      ON B.id = C.bid
    JOIN A.id = B.aid
WHERE A.ownedBy = @userID
  AND D.id = @idToBechecked  

【讨论】:

  • 非常正确。不幸的是,这种方法需要 D 知道,它依赖于 C,它依赖于 B.... “编辑 D”控制器不应该知道它所依赖的其他实体的任何信息。理想情况下,如果我将“编辑 D”控制器模式化到另一个项目,它应该仍然能够保持不变 - 即使实体类型 C 不再存在并且关系 B:D 是 1:n。换句话说,我不想强​​迫 D 的控制器完全抽象,并且对 D 的关系一无所知。
  • @Stefan:在这种情况下,您可能可以使用分层方法。有关各种选项,请参见此处:*.com/questions/4048151/…
  • 谢谢!是的,我知道这样的结构。顺便说一句,这个问题出现在 ORM 的实施过程中。