網站后台權限設計


頭一次寫博客,可能章法有些亂,大家將就看下吧。

(圖太大,建議下載下來看)

一,前言

公司網站的后台是和其它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         }

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM