权限管理系统之角色管理
角色是用户权限的基础。基于角色的访问控制方法还可以很好的地描述角色层次关系,实现最少权限原则和职责分离的原则。角色管理模块主要是针对平台用户的角色资源进行管理:①角色组和角色的设置,②角色的分类归属,③角色的授权(模块资源授权和动作权限分配)
4.2 角色管理
角色是用户权限的基础。
角色管理主要是针对平台用户的角色资源进行管理。主要包括了一下3块功能:
① 角色组和角色的设置,
② 角色的分类归属,
③ 角色的授权(模块资源授权和动作权限分配)。
相对于角色是给用户的权限资源类别分了个组别,角色组的概念其实也就是一种根据角色相近性进行分组归类的方式,本质上没有引入新技术,只是为了方便管理。同时为了降低复杂性,平台最终统一使用了角色来对用户进行分类授权。
模块资源授权也称菜单控制,由业务功能模块列表和用户菜单定制共同组成。每个用户可以拥有自己的菜单,也可以直接采用角色缺省菜单(当用户同时充当多个角色且权限重复时,重复的权限仅一次有效)。
动作权限分配也叫对象控制,对象是指应用系统窗口中的可视对象,如菜单项、按钮、下拉列表框、数据编辑控件及数据编辑控件的字段等。
控制动作是通过角色与用户授权来实现的。主要是对象属性的控制(使能、禁止,可视、屏蔽)和数据编辑控件中数据记录的维护(增加、删除、修改)。
基于角色的访问控制方法还可以很好的地描述角色层次关系,实现最少权限原则和职责分离的原则。
角色管理的功能作业区(用户操作)界面如下图4.2-1所示,通过主要的操作按钮和信息内容可知其功能有:新增角色组、新增角色,编辑角色组、编辑角色,删除角色组、删除角色,角色授权,角色组排序、角色排序等。其中功能操作按钮的状态会根据选择的左侧树型中不同节点和不同登录用户的动作权限而改变。保证了系统规范和权限系统的双重控制。
图4.2-1 角色管理界面
树形的角色、角色组代码核心算法主要由以下代码完成:
#region LoadRoleTypes, LoadRoles private void LoadRoleTypes(TreeNode currentNode, DMESYS_ROLE_TYPE currentRoleType) { List<DMESYS_ROLE_TYPE> subRoleTypes = null; if (currentRoleType != null) subRoleTypes = DBOSYS_ROLE_TYPE.GetSubRoleTypes(currentRoleType); else subRoleTypes = DBOSYS_ROLE_TYPE.GetAllTopRoleTypes(); foreach (DMESYS_ROLE_TYPE rt in subRoleTypes) { TreeNode node = currentNode.ChildNodes.Add(); node.Tag = rt; node.Text = rt.Name; LoadRoleTypes(node, rt); } } private void LoadRoles() { roles.Clear(); roles = DBOSYS_ROLE.GetSYS_ROLEEntities(); foreach (DMESYS_ROLE r in roles) { TreeNode tn = CallFindNode(r.Role_Type_Id, tvRoles); if (tn != null) { TreeNode node = tn.ChildNodes.Add(); node.Tag = r; node.Text = r.Name; } } } #endregion
角色组实体类代码如下所示:
[Serializable, Class("DMESYS_ROLE_TYPE", "Id")] public class DMESYS_ROLE_TYPE : DMEBase, IEquatable<DMESYS_ROLE_TYPE> { #region Private Members private string _id; private string _parent_id; private string _name; .........
角色实体类的代码如下所示:
[Serializable, Class("DMESYS_ROLE", "Id")] public class DMESYS_ROLE : DMEBase, IEquatable<DMESYS_ROLE> { #region Private Members private string _id; private string _role_type_id; private string _name;
.........
角色组操作类的代码如下所示:
#region 从数据表获取记录 GetSYS_ROLE_TYPEEntities, GetSYS_ROLE_TYPEEntity /// /// 获取所有实体对象实例。 /// /// 所有实体对象实例。 public static List<DMESYS_ROLE_TYPE> GetSYS_ROLE_TYPEEntities() { string table = DBOService.GetTableName(typeof(DMESYS_ROLE_TYPE)); string sql = "SELECT * FROM " + table; List<DMESYS_ROLE_TYPE> result = new List<DMESYS_ROLE_TYPE>(); List iIBList = new List(); DataTable dt = DBMsSqlManager.ExecuteDataTable(sql, table); iIBList = DBOService.DataTable2DMEList(dt, typeof(DMESYS_ROLE_TYPE)); foreach (IIdentifiedBase iIB in iIBList) { result.Add(iIB as DMESYS_ROLE_TYPE); } return result; } /// /// 获取实体对象(根据主键)。 /// public static DMESYS_ROLE_TYPE GetSYS_ROLE_TYPEEntity(string dmeId) { if (String.IsNullOrEmpty(dmeId)) return null; IIdentifiedBase iIB = DBOService.LoadEntity(typeof(DMESYS_ROLE_TYPE), dmeId); return iIB == null ? null : (iIB as DMESYS_ROLE_TYPE); } #endregion
角色的操作类代码如下所示:
#region 从数据表获取记录 GetSYS_ROLEEntities, GetSYS_ROLEEntity /// <summary> /// 获取所有实体对象实例。 /// </summary> /// <returns>所有实体对象实例。</returns> public static List<DMESYS_ROLE> GetSYS_ROLEEntities() { string table = DBOService.GetTableName(typeof(DMESYS_ROLE)); string sql = "SELECT * FROM " + table + " ORDER BY Order_id"; List<DMESYS_ROLE> result = new List<DMESYS_ROLE>(); List<IIdentifiedBase> iIBList = new List<IIdentifiedBase>(); DataTable dt = DBMsSqlManager.ExecuteDataTable(sql, table); iIBList = DBOService.DataTable2DMEList(dt, typeof(DMESYS_ROLE)); foreach (IIdentifiedBase iIB in iIBList) { result.Add(iIB as DMESYS_ROLE); } return result; } /// <summary> /// 获取实体对象(根据主键)。 /// </summary> public static DMESYS_ROLE GetSYS_ROLEEntity(string dmeId) { if (String.IsNullOrEmpty(dmeId)) return null; IIdentifiedBase iIB = DBOService.LoadEntity(typeof(DMESYS_ROLE), dmeId); return iIB == null ? null : (iIB as DMESYS_ROLE); } #endregion
上面代码中,数据模型层DML中实体类和数据访问层DAL中访问操作类都实现了序列化。
4.2.1 新增、编辑和删除
新增角色组和角色:点击展开树形菜单“角色类型”,可以看到平台的各个角色组和角色。选择“角色类型”或已有角色组后点击“新增分类”按钮,可以添加角色组;选择新添加的或者已有的角色组,点击“新增角色”按钮,可以添加具体的角色。平台在理论和实现上均支持了角色组和角色的无限递归分类。
在新增角色组或角色的过程中,其中带红色星号标记“*”的条目,比如名称和排序ID是必须要填写的内容。同时会检测该角色组或角色名称在平台上的合法性。确保角色组或角色名称的唯一性和可用性,不至于造成平台管理时的混乱。
具体的操作界面如下图4.2.1-1所示:
图4.2.1-1 新增角色组、角色
编辑角色组和角色:主要是在角色组或角色的名称,排序,备注等信息发生变更的情况时进行的编辑操作。
点击展开树形菜单“角色类型”,在树型目录下选择一个角色组或者角色后点击“编辑”按钮,即完成对应角色组或角色信息的修改。
具体编辑操作界面如下图4.2.1-2中所示:
图4.2.1-2 编辑角色组、角色
删除角色组和角色:如果需要对平台上的角色组或者角色进行删除,点击展开树形菜单“角色类型”,在树型目录下选择一个角色组或者角色后点击“删除”按钮,在图4.2.1-3中显示了几个删除操作的提示对话框界面。
备注:对于已经被使用的角色,不能对其进行直接删除,要先回收角色的使用源;对于拥有子角色组或者角色的角色组是无法直接删除的,首先需要删除下面的子角色组或者下属的角色;对于已经含有权限的角色也是无法直接删除的,也是需要先取消其授权的权限资源后才能进行删除操作。
图4.2.1-3 删除角色组或角色提示
4.2.2 移动角色
平台上的角色组和角色在使用过程中可能会发生归属或者分组的变更,此时就需要涉及到对角色组或角色的进行移动操作。
点击展开树形菜单“角色类型”,选择一个角色组或角色后点击“移动”按钮,如下图4.2.2-1所示,操作界面以动态树形方式列出了当前平台的角色分类结构,选择需要移动到的目标角色组,点击确认,即可完成角色组或角色的调动。
图4.2.2-1 移动角色组或角色
移动角色组或角色功能代码其实也比较简单了,这里就直接上代码了:
private bool flag = false; private void TargetContainsDest(DMESYS_MODULE_TYPE target, DMESYS_MODULE_TYPE dest) { List<DMESYS_MODULE_TYPE> subModuleType = DBOSYS_MODULE_TYPE.GetSubModuleTypes(target); if (subModuleType.Count > 0) { foreach (DMESYS_MODULE_TYPE mt in subModuleType) { if (mt.Id == dest.Id) { flag = true; break; } if (DBOSYS_MODULE_TYPE.GetSubModuleTypes(mt).Count > 0) { foreach (DMESYS_MODULE_TYPE mt2 in subModuleType) { TargetContainsDest(mt2, dest); } } } } } private bool MoveModuleType() { if (target is DMESYS_MODULE_TYPE) { if (destObj != null) { DMESYS_MODULE_TYPE dmeTarget = target as DMESYS_MODULE_TYPE; DMESYS_MODULE_TYPE dmeDest = destObj as DMESYS_MODULE_TYPE; TargetContainsDest(dmeTarget, dmeDest); if (dmeTarget.Id != dmeDest.Id && !flag) { dmeTarget.Parent_Id = dmeDest.Id; DBOSYS_MODULE_TYPE.UpdateDMESYS_MODULE_TYPE(dmeTarget); return true; } else if (dmeTarget.Id == dmeDest.Id || flag) { lbTip.Visible = true; return false; } } else if (destObj == null) { DMESYS_MODULE_TYPE mt = target as DMESYS_MODULE_TYPE; mt.Parent_Id = ""; DBOSYS_MODULE_TYPE.UpdateDMESYS_MODULE_TYPE(mt); return true; } } else if (target is DMESYS_MODULE) { if (destObj == null) { lbTip.Visible = true; } else if (destObj != null) { DMESYS_MODULE dmeModule = target as DMESYS_MODULE; dmeModule.Module_Type_Id = (destObj as DMESYS_MODULE_TYPE).Id; DBOSYS_MODULE.UpdateDMESYS_MODULE(dmeModule); return true; } } return false; }
4.2.3 角色授权
角色授权功能就是为了方便用户权限的统一授权和管理而开发的。用户可根据被设置不同角色来访问本平台上的模块资源与动作权限。
点击展开树形菜单“角色类型”,在角色组下选择一个角色(如“系统管理员”)后点击“授权”按钮。界面以动态树形的方式列出了当前平台中所有模块和对应动作的资源结构。该资源结构是根据模块管理的相关配置加载的,模块允许的权限动作都会在该资源结构中加载。打开页面时会自动加载(显示为已勾选上)当前选中角色已经分配拥有的模块资源和权限动作。点击勾选或者取消勾选对应的模块资源和权限动作,点击确认,即可完成对角色的授权。
具体操作界面如下图4.2.3-1中所示:
图4.2.3-1 角色授权
看完效果,上硬菜了:
#region LoadModuleTypes, LoadModules, LoadGrantedModuleRights, LoadModuleRight // 加载模块类型 private void LoadModuleTypes(TriStateTreeNode currentNode, DMESYS_MODULE_TYPE currentModuleType) { List<DMESYS_MODULE_TYPE> subModuleTypes = null; if (currentModuleType != null) subModuleTypes = DBOSYS_MODULE_TYPE.GetSubModuleTypes(currentModuleType); else subModuleTypes = DBOSYS_MODULE_TYPE.GetAllTopModuleTypes(); foreach (DMESYS_MODULE_TYPE rt in subModuleTypes) { TriStateTreeNode node = new TriStateTreeNode(); node.Tag = rt; node.Text = rt.Name; node.IsContainer = true; currentNode.Nodes.Add(node); LoadModuleTypes(node, rt); } } // 加载模块 private void LoadModules() { modules.Clear(); modules = DBOSYS_MODULE.GetSYS_MODULEEntities(); foreach (DMESYS_MODULE m in modules) { TriStateTreeNode tn = CallFindNode(m.Module_Type_Id, rootNode); if (tn != null) { TriStateTreeNode node = new TriStateTreeNode(); node.Tag = m; node.Text = m.Name; node.IsContainer = true; tn.Nodes.Add(node);
LoadModuleRight(node); } } } // 加载已经授权的模块数据 private void LoadGrantedModuleRights(TriStateTreeNode triStateNode) { foreach (TriStateTreeNode tn in triStateNode.Nodes) { if (tn.Tag is DMESYS_MODULE) { List<DMEV_SYS_RMR> rmrGrantedList = DBOV_SYS_RMR.GetSYS_RMR(role, tn.Tag as DMESYS_MODULE); foreach (DMEV_SYS_RMR rmr in rmrGrantedList) { foreach (TriStateTreeNode n in tn.Nodes) { if (rmr.Right_Id == n.Name && rmr.Role_Id == role.Id) { n.Checked = true; } } } } LoadGrantedModuleRights(tn); } } // 加载具体模块对应的动作权限 private void LoadModuleRight(TriStateTreeNode node) { List<DMESYS_MODULE_RIGHT> mrHadList = DBOSYS_MODULE_RIGHT.GetModuleRights(node.Tag as DMESYS_MODULE); foreach (DataRow dr in ra.Rows) { foreach (DMESYS_MODULE_RIGHT mr in mrHadList) { if (mr.Right_Tag == dr["RAction"].ToString()) { TriStateTreeNode n = new TriStateTreeNode(); n.Text = dr["RActionName"].ToString(); n.Name = mr.Id; node.Nodes.Add(n); } } } } #endregion
上面具体的源代码按照逻辑顺序一步步实现了角色授权的功能,主要通过加载模块类型,加载模块,加载已经授权的模块数据,加载具体模块对应的动作权限等操作,完成了角色的模块授权和动作分配功能。