AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理、职称管理、部门管理、角色管理、角色权限管理等模块。 php
AppBox v2.0中权限管理中涉及三个概念:模块、用户、角色 html
1. 权限是定义在模块中,而模块至关于一个分组,好比用户管理就是一个模块。用户分组模块能够包含的多个页面,好比用户列表页面、新增用户页面、修改用户页面、用户详细信息查看页面、修改用户密码页面等; sql
2. 角色拥有对权限的控制,能够设置一个角色拥有哪些权限; 数据库
3. 一个用户能够有多个角色,用户最终的权限来本身所属角色的权限集合。 框架
下面看一下在AppBox v2.0中设置角色权限的页面: 数据库设计
经过上面的描述能够看出,“模块”在整个权限设计中并不重要,仅仅至关于权限的一个分组。 ui
“模块”的引入使得系统看起来更加复杂,好比判断一个用户对某个页面是否有浏览权限? spa
明显角色模块权限表的设计会比较复杂,由于每一个模块的权限个数不一样,能够须要特殊的结构。 设计
在AppBox v2.0中,咱们是经过JSON结构保持除浏览权限以外的全部权限。来看下数据库表X_RoleModule的初始化脚本: code
SET IDENTITY_INSERT [dbo].[X_RoleModule] ON INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (311, 1, N'CoreUser', 1, N'{"New":true,"Edit":true,"Delete":true,"ChangePassword":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (312, 1, N'CoreRoleUser', 1, N'{"New":true,"Delete":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (313, 1, N'CoreRoleModule', 1, N'{"Edit":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (314, 1, N'CoreRole', 1, N'{"New":true,"Edit":true,"Delete":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (315, 1, N'CorePassword', 1, N'{"Edit":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (316, 1, N'CoreOnlineUser', 1, N'') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (317, 1, N'CoreMenu', 1, N'{"New":true,"Edit":true,"Delete":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (318, 1, N'CoreLog', 1, N'{"Delete":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (319, 1, N'CoreJobTitle', 1, N'{"New":true,"Edit":true,"Delete":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (320, 1, N'CoreDept', 1, N'{"New":true,"Edit":true,"Delete":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (321, 1, N'CoreConf', 1, N'{"Edit":true}') INSERT [dbo].[X_RoleModule] ([Id], [RoleId], [ModuleName], [CanRead], [Others]) VALUES (322, 1, N'AppFile', 1, N'')
这种复杂性不只在数据库设计阶段,在代码编写阶段也不大好处理,须要大量的代码维护权限列表的读写操做。
下面的代码是更新角色对应的模块权限列表的后台代码:
FineUI.CheckBoxField canReadField = Grid2.FindColumn("CanRead") as FineUI.CheckBoxField; XRoleModuleCollection roleModules = new XRoleModuleCollection(); foreach (GridRow row in Grid2.Rows) { int rowIndex = row.RowIndex; object[] dataKeys = Grid2.DataKeys[rowIndex]; // 当前行对应的模块名称 //int moduleId = Convert.ToInt32(dataKeys[0]); string moduleName = dataKeys[1].ToString(); bool canRead = canReadField.GetCheckedState(rowIndex); AspNet.CheckBoxList ddlOthers = (AspNet.CheckBoxList)Grid2.Rows[rowIndex].FindControl("ddlOthers"); JObject otherPowerObj = new JObject(); foreach (AspNet.ListItem item in ddlOthers.Items) { if (item.Selected) { otherPowerObj.Add(item.Value, true); } } if (canRead || otherPowerObj.Count > 0) { XRoleModule roleModule = new XRoleModule(); roleModule.RoleId = roleId; roleModule.ModuleName = moduleName; roleModule.CanRead = canRead; if (otherPowerObj.Count > 0) { roleModule.Others = otherPowerObj.ToString(Newtonsoft.Json.Formatting.None); } else { roleModule.Others = ""; } roleModules.Add(roleModule); } } roleModules.BatchSave();
在这段代码中,咱们不只须要更新浏览权限,还须要将其余权限生成JSON字符串,并插入数据库。
此外因为咱们还要维护菜单列表,在菜单和模块的关系上用户也产生了疑问,来看下编辑菜单的页面截图。
用户须要进一步地了解以下概念:
1. 一个模块能够对应多个页面;
2. 一个页面能够是菜单项,也能够不是菜单项;
3. 须要指定一个菜单项所属的模块,以便根据权限定义生成左侧菜单项。
我不清楚这个概念以前是否有人提过,不过这是独立思考的结果,所以我就给他起了个响亮的名字 - “扁平化的权限设计”。
之因此是扁平化,是由于咱们舍弃了“模块”的页面,因此的权限(在其余系统中可能称之为功能点)均可以单独定义,而每一个页面只须要在这个很大的权限集合中选中本身须要的权限子集(一般,在实践中这个过程是相反的:也便是每一个页面定义本身须要的权限集合,全部页面的权限集合造成了整个站点的权限集合)。
扁平化的权限设计示意图:
数据库设计简单,天然代码就简单了。前面足足 40 多行的保存权限的代码,如今不用 20 行就实现了:
// 当前角色新的权限列表 List<int> newPowerIDs = new List<int>(); for (int i = 0; i < Grid2.Rows.Count; i++) { AspNet.CheckBoxList ddlPowers = (AspNet.CheckBoxList)Grid2.Rows[i].FindControl("ddlPowers"); foreach (AspNet.ListItem item in ddlPowers.Items) { if (item.Selected) { newPowerIDs.Add(Convert.ToInt32(item.Value)); } } } Role role = DB.Roles.Include(r => r.Powers).Where(r => r.ID == roleId).FirstOrDefault(); ReplaceEntities<Power>(role.Powers, newPowerIDs.ToArray()); DB.SaveChanges();
为了不权限集合过于分散,咱们还为每一个权限定义了 GroupName (分组属性),从而在前台展现时更美观,更简洁。
权限表的模型类:
public class Power { [Key] public int ID { get; set; } [Required, StringLength(50)] public string Name { get; set; } [StringLength(50)] public string GroupName { get; set; } [StringLength(200)] public string Title { get; set; } [StringLength(500)] public string Remark { get; set; } public virtual ICollection<Role> Roles { get; set; } }
保存角色权限的页面截图:
虽然界面和以前的差很少,但内部实现已经简化了不少。
菜单项编辑时,只须要指定菜单项对应的浏览权限便可(若是不指定浏览权限,则默认这个菜单项不参与权限控制),以下图所示。
1. AppBox v2.0 是免费软件,免费提供下载:http://fineui.com/bbs/forum.php?mod=viewthread&tid=3788
2. AppBox v3.0 是捐赠软件,你能够经过捐赠做者来获取AppBox v3.0的所有源代码(http://fineui.com/donate/)。