ACL的数据库模式

问题描述 投票:27回答:3

我想为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还有其他架构布局吗?

mysql acl
3个回答
29
投票

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

例如,假设您正在设计社区的架构,并允许用户切换其配置文件的可见性。

一种选择是坚持公开/私人档案标志并坚持广泛的,先发制人的权限检查:'users.view'(观看公共用户)vs,比如'users.view_all'(查看所有用户,对于版主) 。

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

无论使用哪种方法,我都发现角色编辑/分配的复杂性增加了由于为各个数据分配权限所带来的轻松/灵活性,并且以下方面效果最佳:

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

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

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

您可以扩展对单个节点的检查(即check_perms($user, 'users.edit', $node) ,“can can this node”vs check_perms($user, 'users.edit') for“may edit a node” )如果您需要,您将拥有非常灵活/易于使用的最终用户。

正如开头示例所示,要警惕过多地转向行级权限。 检查单个节点的权限的性能瓶颈少于拉动有效节点列表(即只有用户可以查看或编辑的节点)的性能瓶颈。 如果你不是(非常)精通查询优化,我会建议除了行本身之外的标志和user_id字段之外的任何内容。


8
投票

这意味着我可以公然无视数据规范化,因为我永远不会有超过几百条可能的记录。

您期望的行数不是选择要求的正常形式的标准。 规范化涉及数据完整性。 它通常通过减少冗余来提高数据完整性。

要问的真正问题不是“我将拥有多少行?”,而是“数据库始终能给我正确的答案有多重要?” 对于将用于实现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 = ?)

1
投票

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

CREATE TABLE permission (
  id integer primary key autoincrement
  ,name varchar
  ,perm SET('create', 'edit', 'delete', 'view')
  ,resource_id integer );
© www.soinside.com 2019 - 2024. All rights reserved.