网站后台权限设计

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

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

一,前言

公司网站的后台是和其它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,width: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         }

 

 

 

 

posted @ 2013-03-06 17:25  越笨越爱  阅读(8134)  评论(44编辑  收藏  举报