设计用户角色和权限系统的最佳实践是什么?

53

我需要在使用PHP/MySQL构建的Web应用程序中添加用户角色和权限系统。 我希望具备以下功能:

  1. 一个根用户可以创建子根、组、规则和普通用户(所有权限)。
  2. 子根只能为自己的组创建规则、权限和用户(没有组)。
  3. 用户可以访问由他或他的组创建的内容,基于组根分配给他的权限。

我需要这个系统足够灵活,以便可以为内容分配新的角色和权限。

我有一个名为users的表,存储了组键以及其他信息。 目前,我在每个内容表中使用两个字段,即createdByCreatedByGroup,并将其作为某个特定用户是否具有权限的点。 但它不够灵活,因为对于每个新的内容,我都必须经过所有数据更新和权限更新。 请通过讨论您的架构设计最佳实践来帮助我。

5个回答

46

我认为位运算符是实现用户权限的最佳方式。 这里我将展示如何在MySQL中实现它。

下面是一个包含一些样例数据的示例表:

表1:Permission(权限)表,存储权限名称及其对应的位值,例如1、2、4、8等(都是2的倍数)。

CREATE TABLE IF NOT EXISTS `permission` (
  `bit` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`bit`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

向表中插入一些示例数据。

INSERT INTO `permission` (`bit`, `name`) VALUES
(1, 'User-Add'),
(2, 'User-Edit'),
(4, 'User-Delete'),
(8, 'User-View'),
(16, 'Blog-Add'),
(32, 'Blog-Edit'),
(64, 'Blog-Delete'),
(128, 'Blog-View');

表格2:用户表,存储用户ID、名称和角色。 角色将根据权限之和计算。 例如:

如果用户“Ketan”具有“User-Add”(位= 1)和“Blog-Delete”(位= 64)的权限,则角色为65(1 + 64)。 如果用户“Mehata”具有“Blog-View”(位= 128)和“User-Delete”(位= 4)的权限,则角色为132(128 + 4)。

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `role` int(11) NOT NULL,
  `created_date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

示例数据 -

INSERT INTO `user` (`id`, `name`, `role`, `created_date`)
   VALUES (NULL, 'Ketan', '65', '2013-01-09 00:00:00'),
   (NULL, 'Mehata', '132', '2013-01-09 00:00:00');

加载用户权限 登陆后,如果我们想要加载用户权限,则可以查询以下内容以获取权限:

SELECT permission.bit,permission.name  
   FROM user LEFT JOIN permission ON user.role & permission.bit
 WHERE user.id = 1

在这里,user.role "&" permission.bit 是一种按位运算符,其输出结果为 -

User-Add - 1
Blog-Delete - 64

如果我们想要检查一个特定用户是否拥有用户编辑权限 -

  SELECT * FROM `user` 
     WHERE role & (select bit from permission where name='user-edit')

输出 = 没有行。

你还可以看看:http://sforsuresh.in/implemention-of-user-permission-with-php-mysql-bitwise-operators/


权限表的最大允许值是多少?权限表中可以插入多少个唯一的权限?将所有可能的权限总和添加到用户角色列中如何? - traditional
1
@traditional - 使用bigint可以解决大小问题,它是64位的,允许64个权限。如果这已经足够了,那么这是一种不错的做法。如果还不够,并且您不担心空间问题,为每个权限单独设置列可能也可以。 - Autumn Leonard
8
这里没有“角色”的概念——你所拥有的是每个用户的权限列表。 - Gerard ONeill
1
True,在这个实现中没有角色的概念,但这种方法允许您非常容易地检查用户权限的最常见功能,而不需要太多的抽象层。很容易实现一个包含角色和权限数字的表格,其中包括该角色允许的所有权限(就像上面显示的用户一样)。然后,您的用户界面允许将用户分配给一个或多个角色,该角色将该用户的权限设置为所有选定角色的权限值的ORed总和。现在您有了角色。 - Mike
抱歉,但如果您有一个长长的权限列表,可能有一百个,这怎么可行呢?您的数据列将会爆炸。 - tnkh
显示剩余2条评论

39

适合您需要的模式称为基于角色的访问控制

PHP有几个好的实现,包括Zend_Acl(良好的文档),phpGACLTinyACL。大多数框架也以某种形式拥有自己的ACL实现。

即使您选择编写自己的代码,也可以评估这些精心设计的解决方案。


Zend_Acl链接已损坏。 - reformed

7
您可能不想要权限组。相反,创建用户组,给用户组分配权限,并将用户放入组中。用户也应该能够覆盖所在组的权限。如果用户在多个具有权限的组中,则拒绝权限应始终优先于授权。
总之:
- 用户具有零个或多个权限(授权、拒绝)。 - 用户属于零个或多个组。 - 组具有零个或多个权限(授权、拒绝)。

7

我的结构略有不同,但应该可以作为参考。

每个用户都有与之关联的“角色”、“GroupID”,以及 Group 表,其中 GroupID 指的是。 然后我有 3 个权限表。

PermissionMaster(FormName)
PermissionChild(PermissionMasterID, PermissionName, Desc, DefaultValue, DependOn)
该函数表示特定权限的子项,其���参数含义为:父权限ID、子项名称、描述、默认值和对应的依赖关系。
PermissionGroupChild(GroupID, PermissionChildID, Allow)

PermissionMaster保存权限所涉及的名称/表单/模块。PermissionChild将列出每个Master可用的所有可能权限,例如“创建”,“查看”,“编辑”,“删除”以及描述(第一个版本中没有这个,即使为1个模块设置了太多权限也开始变得混乱)。我允许添加更多的子项来具体指定某些功能,如“ChangeTimeStamp”,这也将允许比“Edit”更具体的权限。
然后,PermissionGroupChild是PermissionChild和Group表之间的链接。每个组都会复制一组PermissionChild并设置默认设置。然后我有一个权限类,它会对每个用户进行表查询和检查。我只在登录时加载它。然后在每个表单/模块中,我检查它的适当权限并正确应用UI。
至于角色,我只在登录配置页面使用它。较小的角色值意味着更高的级别。因此,用户只能看到自己和那些角色值比自己高的人。他/她可以编辑低于自己的等级,但不能编辑相似的等级。

请您详细说明一下您如何使用PermissionChild - 我正在处理某些事情,这似乎很有趣。默认值是如何使用的。 PermissionChild(PermissionMasterID,PermissionName,Desc,DefaultValue,DependOn) - automaticAllDramatic
1
对于每个表单,我都有基本的权限:创建(添加新条目)、查看(没有此权限,无法访问表单)、编辑(编辑现有条目)和删除(删除条目)。有时我会有额外的PermissionChild,例如ChangePassword(在用户管理中更改用户密码)等。默认值是一个布尔值,用于指示当不覆盖时,该权限是否默认允许。最常见的表单将具有ViewDefaultValue=1 - faulty

2

我有用户组和用户(类似于Active Directory LDAP解决方案)。因此,如果我授予组访问权限,我需要在这个组中的用户继承访问权限。

基于下面@suresh-kamrushi所提供的答案,我做了以下更改:

INSERT INTO `permission` (`bit`, `name`) VALUES
(1,   'add-yes'),
(2,   'add-no'),
(4,   'edit-yes'),
(8,   'edit-no'),
(16,  'del-yes'),
(32,  'del-no'),
(64,  'view-yes'),
(128, 'view-no');

如果用户的位数是00000000,我会取前两个数字00,这意味着add-yesadd-no从组权限继承而来。
如果用户的位数是01010110,我会取前两个数字01,这意味着add-no将优先使用组权限,因此该用户没有添加权限。这种位方式表示用户只能查看。
它还可以与父组一起使用。
你认为这个解决方案怎么样? 有人有更好的方法吗?

不理解为什么01表示用户没有添加权限。您能否解释一下“这意味着添加-否将在组权限上优先”? - BabyishTank

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