我想为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还有其他架构布局吗?
答案 0 :(得分:29)
根据我的经验,真正的问题主要取决于是否会发生任何数量的用户特定访问限制。
例如,假设您正在设计社区架构,并允许用户切换其个人资料的可见性。
一种选择是坚持使用公共/私人资料标记,并坚持广泛的先发制人权限检查:'users.view'(查看公共用户)vs,比如说'users.view_all'(查看所有用户,主持人)。
另一个涉及更精细的权限,你可能希望他们能够配置事物,这样他们就可以让自己(a)被所有人看到,(b)可以被他们亲自挑选的伙伴看到,(c)完全保密,以及也许(d)所有人都可以看到,除了他们亲手挑选的bozos。在这种情况下,您需要存储各行的所有者/访问相关数据,并且您需要对这些内容进行大量抽象,以避免实现密集,定向图的传递闭包。
无论采用哪种方法,我都发现角色编辑/分配的复杂性增加了为各个数据分配权限所带来的轻松/灵活性,并且以下内容效果最佳:
然后可以在两个查询中提取生成的面向图,使用您使用的任何语言在合理的时间内构建一次,并将其缓存到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 );