AppBox升级进行时 - 扁平化的权限设计
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理、职称管理、部门管理、角色管理、角色权限管理等模块。
AppBox v2.0中的权限实现
AppBox v2.0中权限管理中涉及三个概念:模块、用户、角色
1. 权限是定义在模块中,而模块相当于一个分组,比如用户管理就是一个模块。用户分组模块可以包含的多个页面,比如用户列表页面、新增用户页面、修改用户页面、用户详细信息查看页面、修改用户密码页面等;
2. 角色拥有对权限的控制,可以设置一个角色拥有哪些权限;
3. 一个用户可以有多个角色,用户最终的权限来自己所属角色的权限集合。
下面看一下在AppBox v2.0中设置角色权限的页面:

通过上面的描述可以看出,“模块”在整个权限设计中并不重要,仅仅相当于权限的一个分组。
为什么说AppBox v2.0中的模块是鸡肋!
“模块”的引入使得系统看起来更加复杂,比如判断一个用户对某个页面是否有浏览权限?

明显角色模块权限表的设计会比较复杂,因为每个模块的权限个数不同,可以需要特殊的结构。
在AppBox v2.0中,我们是通过JSON结构保持除浏览权限之外的所有权限。来看下数据库表X_RoleModule的初始化脚本:
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. 需要指定一个菜单项所属的模块,以便根据权限定义生成左侧菜单项。
AppBox v3.0全新的“扁平化权限设计”!
我不清楚这个概念之前是否有人提过,不过这是独立思考的结果,因此我就给他起了个响亮的名字 - “扁平化的权限设计”。
之所以是扁平化,是因为我们舍弃了“模块”的页面,所以的权限(在其他系统中可能称之为功能点)都可以单独定义,而每个页面只需要在这个很大的权限集合中选中自己需要的权限子集(通常,在实践中这个过程是相反的:也即是每个页面定义自己需要的权限集合,所有页面的权限集合形成了整个站点的权限集合)。
扁平化的权限设计示意图:

数据库设计简单,自然代码就简单了。前面足足 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; } }
保存角色权限的页面截图:
虽然界面和之前的差不多,但内部实现已经简化了很多。
菜单项编辑时,只需要指定菜单项对应的浏览权限即可(如果不指定浏览权限,则默认这个菜单项不参与权限控制),如下图所示。

下载或捐赠AppBox
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/)。
喜欢这篇文章,请帮忙点击页面右下角的【推荐】按钮。
后记
感谢各位网友的支持,本文再次荣登【10天推荐排行榜】第一名,上一次登上第一名是:#CSDN刷票门# 有没有人在恶意刷票?CSDN请告诉我!用24小时监控数据说话!

此活动已结束,不会再单独发送PDF文件。
《AppBox升级进行时》前 7 篇文章已经全部在博客公开,请大家直接在我的博客上浏览。后面的文章还没开始写。
AppBox升级进行时 - 扁平化的权限设计的更多相关文章
- AppBox升级进行时 - 拥抱Entity Framework的Code First开发模式
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 从Subsonic到Entity Framework Subsonic最早发布 ...
- AppBox升级进行时 - Entity Framework的增删改查
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. Entity Framework新增数据 以新增用户为例,作为对比,先来看下使 ...
- AppBox升级进行时 - 关联表查询与更新(Entity Framework)
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 关联表的查询操作 使用 Include 方法,我们可以在一次数据库查询中将关联 ...
- AppBox升级进行时 - Attach陷阱(Entity Framework)
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. Attach方法 前面我们已经多次使用Attach方法,上一次使用Attach ...
- AppBox升级进行时 - 如何向OrderBy传递字符串参数(Entity Framework)
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. Entity Framework提供的排序功能 再来回顾一下上篇文章,加载用户 ...
- AppBox升级进行时 - Any与All的用法(Entity Framework)
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 属于某个角色的用户列表(Any的用法) 使用Subsonic,我们有两种方法获 ...
- docker跨主机通信扁平化网络的设计与实现
端口映射.ovs. fannel,weave 1.使用网桥实现跨主机容器连接 使用Open vSwitch实现跨主机容器连接
- 扁平化设计的最新趋势 – 长阴影(Long Shadow)
随着互联网的发展,网页设计变得越来越复杂,如今设计的外观和感觉实现网站功能说使用的开发技术一样重要.互联网的功能远远不只是基本的信息共享,现在人们对网站的期望是远远大于几年前的. 如今,HTML5 & ...
- Flatty Shadow图标自动产生器——在线生成各种扁平化 ICON
在扁平化风格越来越流行的今天,网页.软件界面和图标的设计大都采用了扁平化风格.特别是扁平化图标的设计,摒弃了一切3D元素的设计,阴影.纹理.透视神马的统统不要,让图标简洁高效,富有现代感. 今天给大家 ...
随机推荐
- 如何使用github搭建个人博客
1.去github官网注册个人帐号:没有的:猛戳这里去注册,比如我的账户名:wjf444128852,我的已经汉化(可在github里搜索github如何汉化有插件) 2.点击仓库-新建,仓库名字必须 ...
- iOS UITableView删除cell分割线
UITableView是UITableViewStylePlain风格的,这样整个TableView都会被分割线分隔开,不管有没有数据,非常丑. 为了可以自定义cell的分割线: 解决方案: 将UIT ...
- Linux vi/vim(转载)
Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主 ...
- 使用NSAssert()和NSParameterAssert调试程序
NSAssert: NSAssert()只是一个宏,用于开发阶段调试程序中的Bug,通过为NSAssert()传递条件表达式来断定是否属于Bug,满足条件返回真值,程序继续运行,如果返回假值,则抛出异 ...
- 优质Android小部件:索尼滚动相册
虽然骚尼手机卖的不怎么样,但是有些东西还是做的挺好的,工业设计就不用说了,索尼的相册的双指任意缩放功能也是尤其炫酷.其桌面小部件滚动相册我觉得也挺好的,比谷歌原生的相册墙功能好多了,网上搜了一下也没发 ...
- 使用Intellij IDEA搭建Ext JsMVC web项目
由于自己从android开发转来学习web开发,最近在学习Jsp,之前接触过一点Extjs,所以用jsp来配合ext试试. Ext JS介绍 extjs是一个javascript框架,它的好处就是有它 ...
- 安装免费的正版Windows10操作系统 - 初学者系列 - 学习者系列文章
Windows 10操作系统是目前Windows平台最新的操作系统,其相对于其它旧版的操作系统是一个比较成功的操作系统.微软直接使用10版本号,跳过了9版本号进行发布这款操作系统,说明windows ...
- mysqldump: Error: Binlogging on server not active
在学习使用mysqldump时,使用mysqldump备份时,遇到了下面两个错误: [root@DB-Server backup]# ./mysql_dump_back.sh Warning: Usi ...
- GConf error:Failed to contact configuration server
Linux系统运行一直正常,但是图形界面使用root账号登录时遇到下面错误,第一次遇到这么怪异的状况 具体错误信息如下所示: GConf error:Failed to contact configu ...
- CMS本质上是什么
2015-121.数据可以任意取和构造,结构也很自由,不一定是“站点-栏目-文章-评论”.2.主要用于显示,前台不产生数据(评论.浏览次数除外).3.在模版进行循环.判断,也比后台写代码要方便很多很多 ...