MySQL:如何实现行级安全(类似于Oracle的Virtual Private Database)?

6
假设我有一些销售各种产品的供应商。因此,基本上,我将拥有以下表:vendorproductvendor_product
如果供应商1将Widget 1添加到product表中,我希望只有供应商1能看到该信息(因为该信息是由供应商1“拥有”的)。对于供应商2也是如此。假设供应商2添加了Widget 2,那么只有供应商2应该看到该信息。
如果供应商1试图添加已经被供应商2输入的Widget 2,则不应在product表中创建重复条目。这意味着,我需要知道供应商2现在也拥有Widget 2
多个信息“所有者”存在的问题是如何处理所有者编辑/删除数据。也许供应商1不再想要Widget 2,但这并不一定适用于供应商2。
最后,我希望能够标记某些记录为“是的,我已经审查过这些数据,并且它是正确的”,以便所有供应商都可以看到这些记录。假设我标记Widget 1为好的数据,则所有供应商现在都可以看到该产品。
似乎解决方案是行级安全性。问题在于,我对其概念或如何在MySQL中实现不太熟悉。非常感谢任何帮助。谢谢。
注意:这个问题在这里有所讨论:Database Design: use composite key as FK, flag data for sharing?。当我提出问题时,我并不确定如何很好地表达问题。希望这次我解释得更清楚了。

你真的想要这个吗?如果供应商1更改产品名称怎么办?供应商2也能看到吗?这种“共享所有权”会给你带来很多麻烦。 - Martin Schapendonk
3个回答

11

MySQL不支持在表上原生实现行级安全性控制。然而,您可以通过视图来实现类似的功能。创建一个视图,只显示给定客户端需要查看的行。然后,只将这些视图授权给该客户端,不要授权底层表的访问权限。

请参阅http://www.sqlmaestro.com/resources/all/row_level_security_mysql/


视图能否解决以下问题:1)确保不存在重复数据,2)允许我标记某些数据以便所有供应商都可以使用? - StackOverflowNewbie
您肯定可以设置视图,使其包含所有供应商需要访问的数据。 - squawknull
为确保不存在重复数据,如果一组列必须始终唯一,则在基础表上创建唯一索引。如果您的用例更加复杂,则可以使用触发器。 - squawknull
说实话,在你的情况下,仅在Mysql方面尝试解决这个问题可能会使事情过于复杂化。如果每个供应商的数据集确实是私有的,并且没有数据共享的情况,那么我会说这将很容易实现。但是,由于您正在混合使用供应商私有和供应商共享数据,因此我的个人方法可能是在应用程序层处理供应商数据隔离,而不是在数据库层处理。 - squawknull
私人和供应商共享数据的混合使个体供应商的生活更加轻松。如果一个供应商已经花时间输入了数据,为什么不让另一个供应商使用呢?反正这是相同的数据。如果数据库层本身就不正确,我不确定如何在应用程序层解决这个问题。 - StackOverflowNewbie
你需要一个数据库结构来支持它,但你不需要数据库层来支持它,因为在代码中你有更多的灵活性。我认为在数据库层面上,你已经有了你所需要的表格。如果这是你想要的,我会提交一个单独的答案,这样你就可以对它进行评论和/或投票。 - squawknull

1

你已经提出了一个供应商、产品和供应商产品映射表。你希望如果两个供应商都想使用同一产品,他们可以共享,但是你不希望有重复的产品。对吗?

如果是这样的话,那么就在标识产品的自然键上定义一个唯一索引/约束(比如产品名称)。

如果一个供应商添加一个不存在的产品,那么就将其插入到产品表中,并通过供应商产品表将其映射到该供应商。

如果该产品已经存在,但是已经被映射到另一个供应商,那么就不要将任何东西插入到产品表中,而是添加另一行映射行,将新的供应商映射到现有产品上(这样现在该产品就被映射到两个供应商)。

最后,当一个供应商删除一个产品时,不要真正地将其删除,只需删除供应商产品引用映射即可。最后,如果没有其他供应商仍在引用某个产品,那么就可以删除该产品。或者,你可以定期运行一个脚本,删除所有不再有供应商引用的产品。

最后,在产品表上设置一个标志,表示你已经审核过该产品,然后使用类似以下的查询来查找特定供应商(我们假设供应商ID为7)可以查看的产品:

select product.*
from product
left join vendor_map
on vendor_map.product_id = product.product_id
where vendor_map.vendor_id = 7
or product.reviewed = 1;

最后,如果一个产品由多个供应商拥有,那么您可以禁止编辑或者在其中一个拥有者尝试编辑时“分割”单个产品成为一个新的独特产品,并允许他们编辑自己的产品副本。他们可能需要修改产品名称,除非您想出其他自然键来基于您的唯一约束。

@squawknull - 只是为了澄清,如果供应商1和供应商2都有“小部件1” - 并且供应商1决定将其更改为“小部件-1”,那么这实际上就成为了一个新产品(因为“小部件1”<>“小部件-1”),对吗? (我想这就是您所说的“分裂”。) - StackOverflowNewbie
@squawknull - 这个解决方案对我来说似乎很有道理。我正在尝试想到它可能失败的情况。我不熟悉行级安全性。你的解决方案是否类似于它? - StackOverflowNewbie
抱歉回复慢了,我必须睡觉了... :) 是的,你理解了我所说的“分割”,基本上,如果有人更改了两个供应商共享的产品,你将把该产品“分割”成两个独立的产品,以便它们可以各自走自己的路。但是,如果你想在产品名称上设置唯一约束,当发生这种情况时,你必须强制他们更改产品名称,使其唯一且不同。 - squawknull
这不是真正的行级安全。RLS 更多地是指拥有多个数据库用户,他们只能看到属于自己的数据。通常根据登录到数据库来管理。如果您有一个共享的软件堆栈将用于所有客户端,则可能变得难以控制(我之前曾被迫这样做)。所以,我通常会说,在这种情况下,更灵活的方法是在应用程序层处理安全性,但必须确保它是强大的。 - squawknull
如果您将为客户提供对数据库的直接登录权限(没有 Ruby、PHP 或其他软件中间层),他们将直接使用 Mysql 客户端,例如 SquirrelSQL 或 Navicat),则此方法可能不起作用。 话虽如此,如果必须这样做,我会尝试为他们提供自己的数据库,但这将是我最后的选择。 - squawknull
将会有一个应用层。所以,也许这个可以工作。谢谢! - StackOverflowNewbie

0
这听起来像是您想要对数据进行规范化。您拥有的是一种1(产品)对多个(供应商)的关系。对于大多数情况,该关系是1:1的,只有在某些情况下才是1:n,并不重要 - 从一般角度来看,它仍然是1:n,因此您应该按照这种方式设计数据库。基本布局可能如下所示:
Vendor Table
VendorId    VendorName    OtherVendorRelatedInformation

WidgetTable
WidgetId    WidgetName    WidgetFlag     CreatorVendor   OtherWidgetInformation

WidgetOwnerships
VendorId    WidgetId      OwnershipStatus     OtherInformation

更新:谁有权做什么是一个业务问题,因此您需要制定所有规则。在上述结构中,您可以标记哪个供应商创建了小部件。在所有权方面,您可以标记所有权的状态,例如

  • CreatorFullOwnership(创建者完全拥有权)
  • SharedOwnership(共享所有权)
  • ...

您需要根据业务规则制定旗帜,然后相应地设计业务逻辑和数据访问部分。


解决方案并没有回答问题。WidgetOwnerships 的问题在于我提到的编辑/删除问题。谁真正“拥有”一个小部件并有权编辑/删除它? - StackOverflowNewbie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接