• 网站后台权限设计


    头一次写博客,可能章法有些乱,大家将就看下吧。

    (图太大,建议下载下来看)

    一,前言

    公司网站的后台是和其它2个同事一起做的,权限这块是最后加上去的,当时是另外一个同事做的。

    后来那位同事离职了,后台在不断修改和增加功能的情况下,页面越来越多,原来的权限设计越来越不能满足需求了。

    主要是因为原来的权限是根据页面地址用正则匹配的,这样就出现一个问题,页面如果增加或减少一个参数,就要去修改正则,这样显的太繁琐。

    于是就想着重新设计一套权限。于是就有了本文。

    二,设计思路

    我的设计思路也是根据页面地址来判断,但分成两部分。第一部分为不带参数的页面地址,第二部分是页面地址上带的参数。

    当用户访问某个页面时

    1,先截取不带参数的地址url,再截取地址中的参数对 params

    2,然后从数据库中取出当前用户的所有权限,根据第1步取得的url去匹配权限列表list(同一地址可能对应几个不同权限,比如添加和修改为同一个页面,但权限又是分开的),如果能匹配到,则继续看第3步,否则表示用户没有该页面访问权限

    3,如果第2步能匹配到权限列表list,则从第1步中取出参数对 params ;循环list,判断每一个权限的参数是否能与params中的参数匹配(正则);如果有一个完全匹配,则说明用户有访问本页面权限。

    三,开始设计

    我把权限大致分为页面级权限(即能不能访问某页面)和功能级权限(即能不能使用某页面上的某功能,如删除等)

    先设计数据库,如下图:

    部分权限如下:

     

     四,判断权限

    1,页面级权限

    如上图,假设当前访问的页面是 http://admin/message/msgDraftList.aspx?t=2&s=1&kk=9

    则根据不带参数的URL:admin/message/msgDraftList.aspx可以匹配到6个权限,这时再根据访问时所带的参数 t=2    s=1  kk=9判断应该属于哪个权限

    很明显应该匹配 RightID=879的权限,kk=9不参与权限判定,因为权限表中并没有以该参数作为权限判定的依据。

    假设上面的地址中参数 s=9,则明显匹配不到任何权限,这时应该判定当前用户没有权限

    2,页面上的功能权限

    页面上的功能如果也需要设置权限,则设为 Right=882这样的,在页面上手动输入该权限的 Code来访问该权限,如果用户有该权限,则应该为true否则应该为false

    3,A页面上链接到B页面的权限

    假设A页面上有链接到B页面的<a href='b.aspx'>带我去B页面</a>, 这时可以通过B页面对应的Code判断是否拥有该页面的权限。

    五,实现

    1,所有后台页面继承自同一页面 AdminPage,在这个页面上判断页面级权限。然后在每个页面上判断功能级权限。

    2,实现代码,贴下我的代码吧:

    View Code
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Web;
      6 using System.Web.UI;
      7 using System.Web.UI.WebControls;
      8 using System.IO;
      9 using System.Text.RegularExpressions;
     10 using System.Collections;
     11 using VGShop.Utility;
     12 using System.Collections.Specialized;
     13 namespace VGShop.Utility
     14 {
     15     /// <summary>
     16     /// 登录状态、权限判断
     17     /// </summary>
     18     public class AdminPage : Page
     19     {
     20         /// <summary>
     21         /// 已登录的管理员
     22         /// </summary>
     23         protected VGShop.Entity.Admin user;
     24         /// <summary>
     25         /// 用户权限
     26         /// </summary>
     27         protected AdminRights UserRight;
     28         /// <summary>
     29         /// 当前页的权限
     30         /// </summary>
     31         AdminRights.SystemRight thisRight = AdminRights.SystemRight.None;
     32         /// <summary>
     33         /// 页面加载之前的事件,主要是实例化已登录的用户和判断权限
     34         /// </summary>
     35         /// <param name="e"></param>
     36         protected override void OnPreLoad(EventArgs e)
     37         {
     38             base.OnPreLoad(e);
     39             user = Session["user"] as VGShop.Entity.Admin;
     40             if (user == null)
     41             {
     42                 System.Configuration.AppSettingsReader asr = new System.Configuration.AppSettingsReader();
     43                 string loginPage = asr.GetValue("loginPage", typeof(string)).ToString();
     44                 Response.Write(string.Format("<script>top.location.href='/admin/{0}';</script>", loginPage));
     45                 Response.End();
     46             }
     47             List<Entity.Rights> allRighs = new List<Entity.Rights>();//数据库中所有的权限列表
     48             user.RightList = this.GetAdminRights(user, ref allRighs);  //查询管理员的权限列表
     49             bool result = this.CheckUrl(Request.Url, user.RightList, allRighs);   // 页面权限
     50             if (user.AdminType == 0)    //如果是普通管理员,则检查权限
     51             {
     52                 this.UpAdmin();             //检查当前管理员信息是否被修改过
     53                 if (!result)
     54                 {
     55                     string js = "<script>var noneRightTip = {msg:\"<font color='blue'>您没有权限访问本页,请联系管理员!<br />本页地址:\"+location.href+\"</font>\",fun:function(){}};if (parent.$ && parent.$.jBox) {parent.$.jBox.closeTip();parent.$.jBox.error(noneRightTip.msg, \"无权访问\",{closed:noneRightTip.fun,500});} else {if(!alert(noneRightTip.msg)){noneRightTip.fun();};}</script>";
     56                     Response.Write(js);
     57                     Response.End();
     58                     return;
     59                 }
     60             }
     61             UserRight = new AdminRights(user.RightList, user.AdminType == 1);   //用户的权限用于页面上
     62             UserRight.CurrentPageRight = thisRight;
     63         }
     64 
     65         /// <summary>
     66         /// 检查地址是否有权限
     67         /// </summary>
     68         /// <param name="url">地址</param>
     69         /// <param name="list">用户的权限列表</param>
     70         /// <returns></returns>
     71         bool CheckUrl(Uri url, List<Entity.Rights> list, List<Entity.Rights> allRights)
     72         {
     73             if (url == null)    //如果地址为空,则返回false
     74                 return false;
     75             string lastUrl = url.AbsolutePath.TrimStart('/').ToLower();
     76             var rightList = allRights.Where(a => a.Path.TrimStart('/').ToLower() == lastUrl);
     77             int count = rightList.Count();
     78             if (count == 0) //根据当前地址没有找到对应的权限时,则本地址没有权限访问
     79                 return false;
     80             NameValueCollection cols = new NameValueCollection();
     81             #region 获取参数对
     82             if (!url.Query.IsNullOrWhiteSpace())
     83             {
     84                 string[] arr = url.Query.TrimStart('?').Split('&');
     85                 foreach (var item in arr)
     86                 {
     87                     if (!item.IsNullOrWhiteSpace())
     88                     {
     89                         string[] temp = item.Split('=');
     90                         if (temp.Length == 2)
     91                         {
     92                             cols.Add(temp[0].ToLower(), temp[1]);
     93                         }
     94                     }
     95                 }
     96             }
     97             #endregion
     98             #region 验证权限
     99             Dictionary<int, int> dic = new Dictionary<int, int>();  //Key:正面循环中的i,Value:i对应的权限匹配的参数个数
    100             for (int i = 0; i < count; i++)
    101             {
    102                 int correct = 0;    //已经匹配正确的参数数量
    103                 bool result = false;
    104                 int ruleContainsKey = 0;
    105                 var current = rightList.ElementAt(i);   //当前循环的权限
    106                 result = this.CheckParamAndValue(cols, current.Param1, current.Value1, ref ruleContainsKey); //检查参数1
    107                 if (!result)    //不匹配
    108                     continue;
    109                 correct += ruleContainsKey;
    110                 result = this.CheckParamAndValue(cols, current.Param2, current.Value2, ref ruleContainsKey); //检查参数2
    111                 if (!result)    //不匹配
    112                     continue;
    113                 correct += ruleContainsKey;
    114                 result = this.CheckParamAndValue(cols, current.Param3, current.Value3, ref ruleContainsKey); //检查参数3
    115                 if (!result)    //不匹配
    116                     continue;
    117                 correct += ruleContainsKey;
    118                 result = this.CheckParamAndValue(cols, current.Param4, current.Value4, ref ruleContainsKey); //检查参数4
    119                 if (!result)    //不匹配
    120                     continue;
    121                 correct += ruleContainsKey;
    122                 dic.Add(i, correct);
    123             }
    124             if (dic.Count > 0)
    125             {
    126                 //Response.Write("<script>alert('匹配的权限有"+dic.Count+"个');</script>");
    127                 int index = dic.OrderByDescending(a => a.Value).First().Key;  //如果有多个相匹配的权限,则取匹配参数最多的一个
    128                 Entity.Rights right = rightList.ElementAt(index);
    129                 if (list.Exists(a => a.RightID == right.RightID))   //如果当前筛选出的权限在用户的权限中,则说明用户有权限,否则说明用户没有该权限
    130                 {
    131                     thisRight = (AdminRights.SystemRight)Enum.Parse(typeof(AdminRights.SystemRight), right.Code, true);
    132                     return true;
    133                 }
    134             }
    135             #endregion
    136             return false;
    137         }
    138         /// <summary>
    139         /// 检查该权限的指定参数是否匹配规则,如果权限中不包含该参数,则false,包含且值不能匹配也为false
    140         /// </summary>
    141         /// <param name="cols">当前地址请求中的所有参数和参数名</param>
    142         /// <param name="key">权限中的参数名</param>
    143         /// <param name="rule">权限中的参数值的规则</param>
    144         /// <param name="ruleContainsKey">规则中是是否存在该参数</param>
    145         /// <returns></returns>
    146         bool CheckParamAndValue(NameValueCollection cols, string key, string rule, ref int ruleContainsKey)
    147         {
    148             ruleContainsKey = 0;
    149             bool result = true; //默认匹配
    150             if (!key.IsNullOrWhiteSpace()) // 1) 如果权限中该参数不为空
    151             {
    152                 string val = cols.Get(key.ToLower());
    153                 if (val != null)          // 2) 如果请求的地址中存在该参数
    154                 {
    155                     ruleContainsKey = 10;   //如果请求的地址中确实存在该参数,则增量为10
    156                     if (!rule.IsNullOrWhiteSpace()) // 3) 如果权限中规则不为空,则用正则匹配
    157                     {
    158                         result = Regex.IsMatch(val, rule, RegexOptions.IgnoreCase);
    159                     }
    160                 }
    161                 else                      // 2) 如果请求的地址中不存在该参数,则不匹配
    162                 {
    163                     result = false;
    164                     //如果规则为空,或者可以匹配空字符串,说明参数允许为空,此时也认为请求的地址中包含该参数,此时为真
    165                     if (rule.IsNullOrWhiteSpace() || (!rule.IsNullOrWhiteSpace() && Regex.IsMatch(string.Empty, rule, RegexOptions.IgnoreCase)))
    166                     {
    167                         ruleContainsKey = 1;//如果请求的地址中并没有该参数,但因为参数可匹配空字符串,则增量为1
    168                         result = true;
    169                     }
    170                 }
    171             }
    172             return result;
    173         }
    174         /// <summary>
    175         /// 查询管理员的权限列表
    176         /// </summary>
    177         /// <param name="user">管理员</param>
    178         /// <param name="rights">系统中所有权限的列表(不论当前用户有没有)</param>
    179         private List<Entity.Rights> GetAdminRights(Entity.Admin user, ref List<Entity.Rights> rights)
    180         {
    181             List<Entity.Rights> list = null;
    182             if (user.AdminType == 1)
    183             {
    184                 rights = list = VGShop.Factory.BLLFactory.CreateRights().GetList(true);    //取得所有权限的列表
    185             }
    186             else
    187             {
    188                 string key = string.Format("admin_rights_{0}", user.AdminID);
    189                 list = Common.CacheAccess.GetCache(key) as List<Entity.Rights>;
    190                 rights = VGShop.Factory.BLLFactory.CreateRights().GetList(true);    //取得所有权限的列表
    191                 if (list == null)
    192                 {
    193                     var bll = VGShop.Factory.BLLFactory.CreateRightMaps();
    194                     List<Entity.RightMaps> roleMaps = bll.GetList(user.RoleIDList, true);   //角色的所有权限
    195                     List<Entity.RightMaps> userMaps = bll.GetList(user.AdminID, false, true);   //用户的所有权限
    196                     List<Entity.RightMaps> maps = roleMaps.Union(userMaps).Where(a => !userMaps.Exists(b => b.RightID == a.RightID && b.Forbid)).Distinct(a => a.RightID).ToList();    //得到当前管理员最终的权限映射关系
    197                     list = rights.Where(a => (!a.Lowest && maps.Exists(b => b.RightID == a.RightID)) || a.Lowest).ToList();   //得到当前管理员最终的所有权限
    198                     Common.CacheAccess.SetCache(key, list);
    199                 }
    200             }
    201             return list;
    202         }
    203         /// <summary>
    204         /// 处理页面异常
    205         /// </summary>
    206         /// <param name="sender"></param>
    207         /// <param name="e"></param>
    208         protected void Page_Error(object sender, EventArgs e)
    209         {
    210             Exception ex = Server.GetLastError();
    211             Tools.WriteErrorLog(ex, true);
    212             if (ex is HttpRequestValidationException)
    213             {
    214                 Response.Write("<h1 style='margin:100px 0 0 0;text-align:center;top:100px'>发生一个错误!<a href='javascript:history.go(-1)'>后退</a></h1>");
    215                 Response.Write("<label style=\"color:Red;\">" + HttpUtility.HtmlEncode(ex.Message) + "</label>");
    216                 Server.ClearError(); // 如果不ClearError()这个异常会继续传到Application_Error()。
    217             }
    218         }
    219 
    220         /// <summary>
    221         /// 输出站点地图和禁用缓存
    222         /// </summary>
    223         /// <param name="writer"></param>
    224         protected override void Render(HtmlTextWriter writer)
    225         {
    226             base.Render(writer);
    227             var node = SiteMap.CurrentNode;
    228             if (node != null)
    229             {
    230                 string script = string.Format("<script defer='defer'>var pagebar=document.getElementById('titleBar');if(pagebar){{pagebar.innerHTML='<img height=\"20\" src=\"' + location.protocol + '//' + location.host + '/{0}/skin/Default/Images/home.png\" width=\"20\" /><a href=\"#\" title=\"Billion牛仔\">Billion牛仔</a>&gt;&gt;<a href=\"#\">{1}</a> &gt;&gt; <a href=\"#\">{2}</a> ';}}</script>", "Admin", node.ParentNode.Title.ClearHtmlTag().ReplaceHtmlTag(), node.Title.ClearHtmlTag().ReplaceHtmlTag());
    231                 writer.WriteLine(script);
    232             }
    233             //禁止缓存
    234             Response.Cache.SetCacheability(HttpCacheability.NoCache);
    235             Response.Expires = 0;
    236             Response.Buffer = true;
    237             Response.ExpiresAbsolute = DateTime.Now.AddSeconds(-1);
    238             Response.AddHeader("pragma", "no-cache");
    239             Response.CacheControl = "no-cache";
    240 
    241         }
    242         /// <summary>
    243         /// 进行信息更新,判断是否进入被修改名单,是:查询最新信息写入Session,否:不操作。
    244         /// </summary>
    245         void UpAdmin()
    246         {
    247             List<int> loginUserList = Application["loginUserList"] as List<int> ?? new List<int>();
    248             //是否被修改了
    249             if (loginUserList.Contains(user.AdminID))
    250             {
    251                 user = VGShop.Factory.DALFactory.CreateAdmin().GetModelByLoginName(user.LoginName);
    252                 loginUserList.Remove(user.AdminID);
    253                 Application.Lock();
    254                 Application["loginUserList"] = loginUserList;
    255                 Application.UnLock();
    256                 Session.Add("user", user);
    257             }
    258         }
    259     }
    260     /// <summary>
    261     /// 系统权限
    262     /// </summary>
    263     public class AdminRights
    264     {
    265         List<Entity.Rights> myRights = null;
    266         bool isSuper = false;
    267         /// <summary>
    268         /// 当前管理员的全部权限
    269         /// </summary>
    270         public List<Entity.Rights> AllRights
    271         {
    272             get { return myRights; }
    273         }
    274         /// <summary>
    275         /// 带参数构造参数
    276         /// </summary>
    277         /// <param name="rights">管理员的权限</param>
    278         /// <param name="superAdmin">是否超级管理员</param>
    279         public AdminRights(List<Entity.Rights> rights, bool superAdmin)
    280         {
    281             myRights = rights;
    282             isSuper = superAdmin;
    283         }
    284         /// <summary>
    285         /// 根据权限代码判断是否拥有该权限,超级管理员输入任意代码均返回true
    286         /// </summary>
    287         /// <param name="key">权限代码,不区分大小写,即Rights.Code</param>
    288         /// <returns>是否拥有该权限</returns>
    289         public bool this[string key]
    290         {
    291             get
    292             {
    293                 if (isSuper)
    294                     return true;
    295                 return myRights.Exists(a => a.Code.Equals(key, StringComparison.CurrentCultureIgnoreCase));
    296             }
    297         }
    298         /// <summary>
    299         /// 当前页面权限枚举
    300         /// </summary>
    301         public SystemRight CurrentPageRight { set; get; }
    302         #region 权限枚举
    303         /// <summary>
    304         /// 权限枚举
    305         /// </summary>
    306         public enum SystemRight
    307         {
    308             /// <summary>
    309             /// 无权限
    310             /// </summary>
    311             None
    312             #region 下面的枚举是从数据库中读取出来生成的,因为太长,就不贴出来了
    313             
    314             #endregion
    315         }
    316         #endregion
    317     }
    318 }

    六,总结
    优点:访问页面时,参数位置可以随意调整,不参与权限判断的参数可以任意增减,并不影响权限,就是说访问页面地址比较灵活。

    同一个页面根据不同参数可以分配成不同权限,像添加/修改这样的页面可以做成一个页面,根据参数来区分权限。

    缺点:每增加一个页面,都要到数据库中添加权限代码,权限枚举也要重新生成(不是手动写的,太长了)。

    页面上功能级权限任需要在页面上手写权限代码来判断权限,如下图:

    View Code
     1  /// <summary>
     2         /// 验证权限
     3         /// </summary>
     4         private void CheckRight()
     5         {
     6             this.btnAddItem.Visible = UserRight["goods_goodsAdd"];
     7             this.btnDelete.Visible = UserRight["G_Delete"];
     8             this.btnOn.Visible  = UserRight["goods_Check"];
     9             this.btnOff.Visible = UserRight["G_CancelSale"];
    10             this.btnSaveSort.Visible = UserRight["G_SaveSort"];
    11         }
  • 相关阅读:
    【RabbitMQ】3、win7下安装RabbitMQ
    【协议】4、http状态码
    【协议】3、HTTP 协议入门
    【协议】2、TCP/IP协议三次握手与四次握手流程解析
    【Dubbo&&Zookeeper】5、dubbo总结和学习资料汇总
    【Dubbo&&Zookeeper】6、 给dubbo接口添加白名单——dubbo Filter的使用
    【Spring】27、JPA 实现乐观锁@Version注解的使用
    HBase 常用Shell命令
    Java操作XML的JAXB工具
    JAXB--@XmlType注解标注xml生成顺序
  • 原文地址:https://www.cnblogs.com/alax/p/2946183.html
Copyright © 2020-2023  润新知