【问题标题】:Database schema for ACLACL 的数据库架构
【发布时间】:2011-08-18 01:16:42
【问题描述】:

我想为 ACL 创建架构;但是,我在几种实现方式之间犹豫不决。

我很确定我不想处理级联权限,因为这会给后端和网站管理员带来很多困惑。

我想我也可以让用户一次只扮演一个角色。像这样的设置将允许在站点增长时根据需要添加角色和权限,而不会影响现有角色/规则。

起初我打算对数据进行规范化,并用三个表来表示关系。

ROLES { id, name }
RESOURCES { id, name }
PERMISSIONS { id, role_id, resource_id }

确定用户是否在某处被允许的查询如下所示:

SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)

然后我意识到我将只有大约 20 个资源,每个资源最多有 5 个操作(创建、更新、查看等),也许还有另外 8 个角色。这意味着我可以公然无视数据规范化,因为我永远不会有超过几百条可能的记录。

所以也许像这样的架构会更有意义。

ROLES { id, name }
PERMISSIONS { id, role_id, resource_name }

这将允许我在单个查询中查找记录

SELECT * FROM permissions WHERE role_id = ? AND permission  = ? ($user_role_id, 'post.update')

那么,哪一个更正确呢? ACL 还有其他架构布局吗?

【问题讨论】:

  • 您也可以使用正确、正确、规范化的模式在单个查询中查找权限。使用JOIN
  • 我对级联权限的直接体验是,我(软件开发人员/维护人员)很难做到正确,但让管理员的生活变得更加美好。部分原因是我在权限 UI 中包含了查看您有权执行的操作以及为什么您有权执行此操作的功能。所以他们可以很容易地看到权限来自哪里。这对他们来说非常直观。

标签: mysql acl


【解决方案1】:

根据我的经验,真正的问题主要分解为是否会发生任何特定于用户的访问限制。

例如,假设您正在设计社区的架构,并且您允许用户切换其个人资料的可见性。

一种选择是坚持公共/私人个人资料标志并坚持广泛的、先发制人的权限检查:“users.view”(查看公共用户)与“users.view_all”(查看所有用户、版主)。

另一个涉及更精细的权限,您可能希望他们能够配置事物,以便他们可以(a)所有人都可以查看,(b)他们精心挑选的伙伴可以查看,(c)完全保密,并且也许(d)除了他们精心挑选的笨蛋之外的所有人都可以看到。在这种情况下,您需要为各个行存储与所有者/访问权限相关的数据,并且您需要大量抽象其中一些内容,以避免具体化密集、有向图的传递闭包。

无论采用哪种方法,我发现角色编辑/分配中增加的复杂性被分配权限给各个数据片段带来的轻松/灵活性所抵消,并且以下效果最好:

  1. 用户可以拥有多个角色
  2. 角色和权限合并在同一个表中,用一个标志来区分两者(在编辑角色/权限时很有用)
  3. 角色可以从同一个表中分配其他角色,并且角色和权限可以分配权限(但权限不能分配角色)。

然后可以在两个查询中提取生成的定向图,使用您使用的任何语言在合理的时间内构建一次,并缓存到 Memcache 或类似物中以供后续使用。

从那里,提取用户的权限是检查他拥有哪些角色,并使用权限图处理它们以获得最终权限。通过验证用户是否具有指定的角色/权限来检查权限。然后根据该权限检查运行您的查询/发出错误。

如果需要,您可以扩展对单个节点的检查(即 check_perms($user, 'users.edit', $node) 用于“可以编辑此节点”vs check_perms($user, 'users.edit') 用于“可以编辑节点”),您将拥有非常灵活/简单的东西供最终用户使用。

正如开头的示例应该说明的那样,请注意不要过多地转向行级权限。性能瓶颈不是检查单个节点的权限,而是提取有效节点列表(即只有用户可以查看或编辑的节点)。如果您不(非常)精通查询优化,我建议您不要在行本身中使用除标志和 user_id 字段之外的任何内容。

【讨论】:

  • 感谢您对更复杂关系的精彩概述。我想这是像 facebook 这样的权限图的类型。我记得不久前和一个人谈过角色,他提到了很多需要多个角色的边缘案例。唯一让我担心的是,将此图存储在内存中所需的对象/数组的大小至少为一兆字节。似乎只查询您需要的部分并将图形留在数据库中会节省大量 RAM。
  • 对于我遇到的权限图,结果实际上很小。但是,我也从来不需要管理数百个角色。 :-)
  • 实际上,我试图想一些单独的对象也需要权限的例子——但我想不出它们应该属于图表的时间。如果它是您所说的用户配置文件权限,我同意配置文件行应该包含一个位列。如果是某个角色的私人论坛,我认为也应该进入论坛对象。您是否还有其他仅适用于角色的情况示例?
  • 亲爱的@Denis,我需要实现你提到的这个结构。你能告诉我表结构是什么,我应该使用什么查询?
  • @DenisdeBernardy 也希望看到一个(示例,简单的)数据库结构来解释您的概念。我想我跟着,但我不完全确定,看到一个简单的数据库表结构会很清楚。谢谢!
【解决方案2】:

这意味着我可以明目张胆地运动 忽略数据规范化,因为我 永远不会超过一对 一百个可能的记录。

您期望的行数不是选择要针对的标准形式的标准。 规范化与数据完整性有关。它通常通过减少冗余来提高数据完整性。

真正要问的问题不是“我将拥有多少行?”,而是“数据库始终给我正确的答案有多重要?”对于将用于实现 ACL 的数据库,我会说“非常重要”。

如果有的话,较少的行数表明您不需要关心性能,因此 5NF 应该是一个简单的选择。在添加任何 ID 号码之前,您需要达到 5NF。

查询用户是否是 允许某处看起来像 这个:

SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions 
WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)

您将其写成两个查询而不是使用内部连接表明您可能会不知所措。 (这是观察,不是批评。)

SELECT p.* 
FROM permissions p
INNER JOIN resources r ON (r.id = p.resource_id AND 
                           r.name = ?)

【讨论】:

  • 5NF 中的 ACL 模式是什么样的?
  • 完全同意,最重要的是表格应该保持清晰。如果您想在速度方面进行优化,那么您应该查看数据库中的位文件,一些具有固定 ACL 元素的应用程序使用位域和位操作来执行 ACL 检查,但这远不如一些具有明确关系和未来扩展的表的可读性可用的。在速度方面,应用程序可以在应用程序级对象中的所有 ACL 表完成初始加载后缓存 ACL 对象,并使用应用程序语言而不是 SQL 执行检查。
【解决方案3】:

您可以使用 SET 来分配角色。

CREATE TABLE permission (
  id integer primary key autoincrement
  ,name varchar
  ,perm SET('create', 'edit', 'delete', 'view')
  ,resource_id integer );

【讨论】:

  • 事先不知道规则。虽然大多数资源都需要基本的 CRUD 规则,但可能还有其他自定义规则,因此 SET 不起作用,因为它们无法在项目开始时定义。
  • 您可以在之后使用ALTER TABLE扩展集合。
  • 好吧,问题是某些模块的规则是独一无二的。也许“修改”是文章的权限,“退款”是支付系统的权限。另外,ALTER TABLE 在大型数据集上不是很快,每次需要添加新权限时都很麻烦。
  • @Xeoncross, IIRC, ALTER TABLE 在其中添加项目在集合的末尾只会导致表头被更新(.frm 文件),而不是表本身.只要集合中的更改不会导致数据大小增长(一个字节最多 8 个集合项,一个字最多 16 个集合项,一个双字最多 32 个等)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
相关资源
最近更新 更多