ACL的数据库模式

时间:2011-05-03 20:58:10

标签: mysql acl

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

3 个答案:

答案 0 :(得分:29)

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

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

一种选择是坚持使用公共/私人资料标记,并坚持广泛的先发制人权限检查:'users.view'(查看公共用户)vs,比如说'users.view_all'(查看所有用户,主持人)。

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

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

  1. 用户可以拥有多个角色
  2. 角色和权限在同一个表中合并,并带有一个标志以区分两者(在编辑角色/烫发时很有用)
  3. 角色可以分配其他角色,角色和权限可以在同一个表中分配权限(但权限不能分配角色)。
  4. 然后可以在两个查询中提取生成的面向图,使用您使用的任何语言在合理的时间内构建一次,并将其缓存到Memcache或类似文件中以供后续使用。

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

    如果需要,您可以扩展对单个节点的检查(例如check_perms($user, 'users.edit', $node)为“可以编辑此节点”与check_perms($user, 'users.edit')“可以编辑节点”),并且您将拥有某些节点非常灵活/易于最终用户使用。

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

答案 1 :(得分: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 = ?)

答案 2 :(得分:1)

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

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