使用用户权限存储菜单项


11

我正在用PHP和MySQL创建菜单系统。我将有几个不同的菜单,每个菜单都将具有一组与其连接的菜单项。

在该网站上,我还具有不同的用户权限,有些用户可以看到所有菜单项,有些项目对某些用户是隐藏的。我很好奇如何以一种干净的方式处理权限,将来可以轻松添加更多类型的用户。

到目前为止,我有这样的事情:

-------------------
|Menus
-------------------
|id| |display name|
-------------------

----------------------------------------------------------
|Menu items
----------------------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort| |permission|
----------------------------------------------------------

我在想该permission列可能是一个逗号分隔的字符串,我可以将其与当前用户的权限ID进行匹配。它也可能是对其他一些表的引用,该表定义了当前现有权限的所有可能组合。

一种解决方案也可以是简单地存储多个菜单项,唯一的区别是权限,尽管这将导致重复存储,并且可能给管理带来麻烦。

我很想听听有关如何组织这个以及什么可以被认为是干净,动态和dynamic脚的思考。

谢谢。


用户有什么共同点吗?通常,您会将菜单项分组为功能组,然后将用户分配给这些组(例如-管理员用户,数据库用户,交易员等)。然后,您可以根据技术选择来管理分组-可以使用Active Directory之类的方法进行管理。
迈克尔

1
您在这里得到了很多不错的答案,但是您可能想读的是ACL。en.wikipedia.org/wiki/Access_control_list
Reactgular

Answers:


18

我将使用ER图对其进行建模。

  • A PERMISSION是授予给ROLE定访问的访问权限MENU_ITEM
  • ROLE是一组预定义的权限,并为其指定了名称
  • A USER可以授予许多角色。
  • 将权限分配给角色而不是用户,使权限的管理更加容易。

在此处输入图片说明

然后,您可以创建一个视图,这样就不必每次都编写联接:

create or replace view v_user_permissions
select
    distinct mi.id, mi.menu_id. mi.label. mi.link, mi.parent, mi.sort, u.user_id
from
    user u
    join user_role ur on (u.user_id = ur.user_id)
    join role ro on (ur.role_id = role.role_id)
    join permission per on (ro.role_id = per.role_id)
    join menu_item mi on (per.metu_item_id = mi.metu_item_id)

然后,每当您想知道用户可以访问哪些菜单项时,都可以对其进行查询:

select * from v_user_permissions up where up.user_id = 12736 order by up.sort

编辑:

由于用户可以被授予多个角色,因此角色的权限可以重叠,即,两个不同的角色可以访问同一菜单项。定义角色时,您事先不知道它是否将具有与其他角色相同的某些权限。但是,由于它与集合的并集有关,因此只关系给定权限是否是集合的一部分,而与它出现的次数无关,因此只关系distinct视图中的子句。


很好,谢谢。不明白为什么我自己不能把它画出来。想我是封锁,非经验丰富的:)
跨度

阿克,我以为我明白了,但显然没有。如何对一个菜单项具有多个权限?
跨度

1
@span因为用户可以被授予多个角色,并且角色的权限可以重叠,即,两个不同的角色可以访问同一菜单项。定义角色时,您事先不知道该角色是否会与其他具有某些权限的角色一起授予。但是,由于此问题与集合的并集有关,因此仅与给定权限是否属于集合有关,而与它出现的次数无关紧要。
TulainsCórdova'13

谢谢,我会继续阅读您的答案,直到我得到它为止;)。我认为我的错误在于认为可以将单个权限与用于分隔菜单项的角色一起使用。似乎我需要菜单项的每个“类型”的许可。再次感谢您的帮助!我会画一些维恩图,看看我能正常O / \避开它我的头
跨度

5

使用逗号分隔的列表意味着每次对菜单进行查询时都要进行子字符串比较。这不理想。

您需要规范化表:

---------------------------------------------
|Menu items
---------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort|
---------------------------------------------

+---------------------------+
| menu_permission           |
|---------------------------|
| |menu_permission_id| (pk) |
| |menu_id| (fk)            |
| |permission|              |
+---------------------------+

如果您仍然希望逗号分隔的列表(出于某种原因),你可以用东西将其拉出,如group_concatMySQL的 wm_concat甲骨文在其他语言或类似的功能。

这样做的好处是多方面的。

首先,有电话的实用性。对任意大的字符串执行子字符串(如果您固定大小,则稍后填充字符串可能会遇到问题,因此您开始获得权限a而不是another_permission)意味着在每一行上扫描该字符串。这不是数据库被优化的东西。

其次,您编写的查询变得简单得多。要确定权限“ foo”是否存在于逗号分隔的列表中,您需要检查“ foo”。

... permission like "%foo%" ...

但是,如果您还具有“ foobar”权限,这将产生误报。所以现在你需要做一个测试

... permission like "%,foo,%" ...

但是,如果'foo'位于字符串的开头或结尾,则将给出错误的否定。导致类似

... (permission like "foo,%" 
  or permission like "%,foo,%" 
  or permission like "%,foo" ) ...

您会注意到,很有可能需要对字符串进行多次扫描。这种方式导致疯狂。

请注意,所有这些都缺乏进行参数绑定的实际能力(仍然可能,但变得更加难看)。

对字段进行规范化可以为您的数据库提供更大的灵活性和可读性。你不会后悔的。


感谢您的出色回答,它为我提供了更多知识,对此我感到非常高兴,尽管我认为user61852解决方案目前最适合。
跨度

3

那个的经典方法是User -> UserGroup然后关联一个Menu -> MenuItem -> UserGroup。使用整数值权衡许可级别。

-------------------
|Menu
-------------------
|id| |display name|
-------------------

-------------------------------------------------------------
|Menu Item
-------------------------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort| | min_option1
-------------------------------------------------------------

-------------------------------------------
| User
-------------------------------------------
|id| | username | password | user_group_id
-------------------------------------------

-------------------------------------------
| User Group
-------------------------------------------
|id| option1 | option2 | option2
-------------------------------------------

当您需要显示当前用户的菜单时。您可以像这样查询数据库。

SELECT * FROM `MenuItem`
    LEFT JOIN `UserGroup` ON (`MenuItem`.`user_group_id` = `UserGroup`.`id`)
    LEFT JOIN `User` ON (`UserGroup`.`id` = `User`.`user_group_id` AND `User`.`id` = $current_user_id)
        WHERE `MenuItem`.`menu_id` = $menu_id AND `UserGroup`.`option1` >= `MenuItem`.`min_option1`;

这样只会根据的条件选择当前用户可见的菜单option1

或者,如果您在当前会话中存储当前用户的组详细信息,则不需要加入。

SELECT * FROM `MenuItem` WHERE `MenuItem`.`menu_id` = $menu_id AND `MenuItem`.`min_option1` >= $user_group_option1;

当您谈论要为每个菜单项存储多个权限时。我会注意不要混淆用户角色和业务逻辑。


2
这种设计将只允许将每个MenuItem绑定到单个UserGroup,这意味着菜单有限或数据重复。实际上,理想情况下,您需要一个链接表。同样,您选择DB表命名为复数也让我很难过;)
Ed James

@EdWoodcock哦,很好。我应该使用一个权限级别(int),然后将其与用户的组级别进行比较。我会改变的。注意,复数名称的习惯是由我使用CakePHP引起的。这很奇怪,因为该框架对查询中的表使用单数别名。
Reactgular

@MatthewFoscarini不用担心,只要代码库保持一致,我就不会感到烦恼;)
Ed James

1
很棒的答案。下次执行此操作时,我会记住这一点。目前,我认为user61852解决方案将是最合适的,因为它不需要对现有代码进行太多更改。谢谢!
跨度
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.