订餐系统之权限设计
大约从两年前开始真正的进入园子,各位园友们的文章真让我受益匪浅,从编程思想,各类工具,代码技巧,管理心得...方方面面太多了。也不知从何时开始萌生了自己写文章的念头,但终于还是不敢写,担心自己的技术不太好,写的文章给园子摸黑了。幸好,有我家妞妞一直从旁鼓励:如果有一部分人,哪怕是一小部分人看了,觉得有益,就为园子做贡献了,当然,如果还有人提出更好的意见或建议,那对自己,对更多人就有益了。于是,借着双休,早上7点就迫不及待爬起来开始真正的“园路”了。不知道各位程序员有没有同感:平时早上总是睡不够,一到放假反倒没瞌睡了。
对于这个标题我想解释下,以免各们觉得我是在做seo,当然也许是我小人之心了。从10年1月22号,开始工作,一直在现在的公司做此系统的开发,程序员嘛,三句话不离老本行,所以用了“订餐系统”作为前缀,我想以后的文章也会是关于这个的方方面面了(下一篇文章应该是订餐系统之按距离[根据经纬度]排序、搜索)。至于权限,其实园子中已经有很多前辈写过很多优秀的东西了,特别是吉日嘎拉,他们都是数年的积累了,才有如此稳定的东西。当然,我们的权限系统也是几年来数十个客户(当然,只对重点客户在用此权限系统 )反馈、升级后的结晶,其中包括台湾某订餐网站及北京某团购旗下订餐网站。也是他们的好评让我有信心再写这个经久不衰的话题。当然,最重要的还是,我觉得此权限系统中每个细节可控和集成轻量(简单,但重复,这也是此文的另一个目的,希望大家指点下),也是自己从设计,到编码一手完成的(还是有点私心哈)。因为第一次写,所以罗嗦了半天,下面开始才真正权限之路了。
首先还是需求:此权限系统可以控制到一个模块能否查询、添加、编辑、删除等操作,另外也可以根据需求添加导出,导入,分配权限等权限控制,按两级分类,如图(1):
图(1)
至于数据表的设计和目前大家看到的表差不了多少,如图(2):
图(2)
管理员表- EAdmin:Rem表示角色编号,Permission表示类型,1表示超级管理员(不受权限控制,提高速度,PS.判断权限是个复杂的过程),0表示普通管理员
角色表-sys_Roles:目前一个管理员只能对应一个角色;
模块表-sys_Module:此表数据就是对应系统的系统中每个模块,如用户管理,订单管理,用户管理又分统计管理,积分管理等。 M_ParentID表示父类编号,M_PageCode用于和sys_RolePermission关联,一级分类形如:S00,S01.... 二有分类形如:S00M01,S00M02....,展现形式如图(3):
图(3)
模块操作权限表-sys_ModulePermition:此表关联sys_Module,记录sys_Module中每个功能所有操作项目,如:查看,添加,编辑,删除,统计等,展现形式如图(4),pvalue表示每个操作项的权限(2的指数次方),用于后面权限的判断。
图(4)
角色-权限关系表-sys_RolePermission:此表保存每个角色对每个模块的相关操作权限,P_Value保存对于某个模块所有操作项对应的权值之禾。
以上便是我们的权限系统涉及的所有表,可能大部分权限的设计应该都差不多吧,差别应该都在控制方式,展现形式上的。除sys_ModulePermition这个设计外,我大部分也是参考的公司购买的一个权限系统,加入我们的元素,客户需求。
目前这个权限系统要应用于某个项目时,同事总有些怨言,集成简单,但要做很多重复的事,如:把每个功能块加到数据库中,再为其添加操作项,如,查询、添加、编辑、删除等,这个也许大家也可以接受,我想任何权限系统都避免不了的。但是,另一点就是我自己都介绍不了的,就是每个操作前,都要添加类似的判断代码,如下面代码中注释的两行:
if (Request["id"] == null) { //判断添加权限 int _rs = WebUtility.checkOperator(2); if (_rs == 0) { AlertScript.RegScript(this.Page, this.UpdatePanel1, "alert('无操作权限','success','true',5);init();"); return; } if (dal.Add(model) > 0) { AlertScript.RegScript(this.Page, UpdatePanel1, "tipsWindown('提示信息','text:保存成功!','250','150','true','1000','true','text');"); } else { AlertScript.RegScript(this.Page, UpdatePanel1, "tipsWindown('提示信息','text:保存失败!','250','150','true','1000','true','text');"); } } else { //判断编辑权限权限 int _rs = WebUtility.checkOperator(3); if (_rs == 0) { AlertScript.RegScript(this.Page, this.UpdatePanel1, "alert('无操作权限','success','true',5);init();"); return; } if (dal.Update(model) > 0) { AlertScript.RegScript(this.Page, UpdatePanel1, "tipsWindown('提示信息','text:保存成功!','250','150','true','1000','true','text');"); } else { AlertScript.RegScript(this.Page, UpdatePanel1, "tipsWindown('提示信息','text:保存失败!','250','150','true','1000','true','text');"); } }
每个操作前都加这样的代码,还要输入每个操作对于权值的指数部分,如代码中的,1,2,3(1,2,3,4分别表示:查,增,修改,删除,其他根据操作项目面定),这样每个系统下来都要用个半天来作这些个简单,重复的事儿,再此真心希望大家给点优化的意见。
接下可能是我自己认为比较核心的部分吧,那就是如果判断当前登录的管理员,是否有权限进行当前操作,因为每个操作的权值都2的指数次方,所有我是通过按位与来判断,代码如下:
/// <summary> /// 操作权限判断,返回0表示不能操作,1表示可以操作 1,2,3,4分别表示:查,增,修改,删除,其他根据操作项目面定 /// </summary> /// <param name="model">当前登录用户</param> /// <param name="type">1,2,3,4分别表示:查,增,修改,删除</param> /// <param name="Permission">需要要的权限</param> public static int checkOperator(int type) { Hangjing.Model.EAdminInfo model = UserHelp.GetAdmin(); int rs = 0; if (model != null) { if (model.Permission == "0") { string filename = GetUrlFileName();//获取页面名称,权限中是根据页面名称来获取模块,从而判断 IList<sys_RolePermissionInfo> rplist = SectionProxyData.GetRolePermissions(model.Rem);//根据角色获取所有相关的模块项目 int p_value = 0; //根据文件名获取当前所在的模块(一个模块包含涉及的页面,可能是多个) System.Globalization.CompareInfo Compare = System.Globalization.CultureInfo.InvariantCulture.CompareInfo; foreach (sys_RolePermissionInfo item in rplist) { int i = Compare.IndexOf(item.des, filename, System.Globalization.CompareOptions.IgnoreCase); if (item.des.IndexOf(filename) >= 0) { p_value = item.P_Value; break; } } if (p_value > 0) { //此模块的权限值与当前操作 查(2),增(4),修改(8),删除(16)按位与,所得结果和操作的值一样,说明有这个操作权限 int cvalue = Convert.ToInt32(Math.Pow(2, type)); if ((p_value & cvalue) == cvalue) { rs = 1; } } } else { rs = 1; } } if (rs == 0) { if (type == 1) { HttpContext.Current.Response.Redirect("~/basic.aspx?msg=1"); } } return rs; }
从早上7:00到下午13:30,终于完成第一篇博客了,不知不觉中就用了几个小时。写是写完了,不过我想,这样肯定对没有了解过权限的园友来说,看起来可能有些麻烦了,对了解权限的园友来说,可能又是不屑一顾的,有点纠结,主要是没法让大家看下效果,亲自体验下,不过,所有截图都是实际项目中的,关键操作流程有截图说明了。
鄙人第一次写博客,各样操作都不是很熟悉,写得不好,大家海涵,如果这篇文章辱了园子名声,下篇:《订餐系统之按距离[根据经纬度]排序,搜索》定要为园子挽回点名声。
成为一名优秀的程序员!