最近本身在作一套權限系統,進展還不錯,在這裏我要感謝兩我的對我權限系統UI上的幫助:第一個是@微軟高級php工程師在博客園看到這位牛人怎麼扣界面的,很是膜拜啊。原文地址:javascript
大溼教我寫.net通用權限框架(1)之菜單導航篇 可是看了個只知其一;不知其二,幸虧沒多久又出現了一位牛人@wolfy,通過他這兩篇文章:php
看過《大溼教我寫.net通用權限框架(1)之菜單導航篇》以後發生的事 css
看過《大溼教我寫.net通用權限框架(1)之菜單導航篇》以後發生的事(續)——主界面html
跟着大師們的步伐,我從登錄界面到主界面,不厭其煩的F12,發現並不像微軟高級php工程師說得那麼簡單,有些東西抓過來並不真的就能夠用,佈局會亂掉,JS也會有不少地方報錯。前端
因此我對像我同樣的菜鳥同行建議,人家方法指出來了,咱們也仍是要提升一下自身的修爲,要學習一下html、div+css佈局、javascript這些東西,否則層次差太遠,人家說稍微高深一點的東西咱們就不懂了。java
固然本身懂得多一點也能夠發現一些牛人們犯的錯誤,牛人畢竟不是聖人。ajax
大半套UI作下來我發現本身頂多只有4分之1的時間是在寫有用的代碼,其它時間基本是在學一些基本的前端知識,當我如今再回過頭去看前面提到的那幾篇博客時收穫要多不少。數據庫
特別是微軟高級php工程師的動態拼接html,用的真是太好了。我把那些東西用到本身的開發中發現解決了不少之前解決不掉的問題。緩存
費話少說曬一下個人成果,這個主界面就是徹底仿的微軟高級php工程師的(固然微軟高級php工程師看到了別跟我急,這是練練手,確定不敢拿了商用,這點我懂的)安全
wolfy已經在他的文章裏講了如何實現,有興趣的能夠參照博文《大溼教我寫.net通用權限框架(1)之菜單導航篇》 我這裏就很少費筆墨了。
我主要說一下帶出菜單項後如何對頁面的權限進行控制
菜單權限表:
CREATE TABLE [dbo].[BPMS_UserMenu]( [UserMenuId] [varchar](50) NOT NULL, [UserId] [varchar](50) NULL, [MenuId] [varchar](50) NULL, [CreateDate] [datetime] NULL, [CreateUserId] [varchar](50) NULL, [CreateUserName] [varchar](50) NULL, CONSTRAINT [PK_BPMS_USERMENU] PRIMARY KEY NONCLUSTERED ( [UserMenuId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶菜單關係主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu', @level2type=N'COLUMN',@level2name=N'UserMenuId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu', @level2type=N'COLUMN',@level2name=N'UserId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'菜單主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu', @level2type=N'COLUMN',@level2name=N'MenuId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'發生時間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu', @level2type=N'COLUMN',@level2name=N'CreateDate' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'建立用戶主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu', @level2type=N'COLUMN',@level2name=N'CreateUserId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'建立用戶' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu', @level2type=N'COLUMN',@level2name=N'CreateUserName' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶菜單關係' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenu' GO ALTER TABLE [dbo].[BPMS_UserMenu] ADD CONSTRAINT [DF_BPMS_UserMenu_CreateDate] DEFAULT (getdate()) FOR [CreateDate] GO
這裏很簡單,就是把用戶表跟菜單表的中間表,用戶用哪些菜單的權限都保存在這個表裏面
按鈕權限表:
CREATE TABLE [dbo].[BPMS_UserMenuButton]( [UserMenuButtonId] [varchar](50) NOT NULL, [UserId] [varchar](50) NULL, [MenuId] [varchar](50) NULL, [ButtonId] [varchar](50) NULL, [CreateDate] [datetime] NULL, [CreateUserId] [varchar](50) NULL, [CreateUserName] [varchar](50) NULL, CONSTRAINT [PK_BPMS_USERMENUBUTTON] PRIMARY KEY NONCLUSTERED ( [UserMenuButtonId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶菜單按鈕關係主 鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'UserMenuButtonId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'UserId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'菜單主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'MenuId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'按鈕主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'ButtonId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'發生時間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'CreateDate' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'建立用戶主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'CreateUserId' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'建立用戶' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton', @level2type=N'COLUMN',@level2name=N'CreateUserName' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶菜單按鈕關係' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'BPMS_UserMenuButton' GO ALTER TABLE [dbo].[BPMS_UserMenuButton] ADD CONSTRAINT [DF_BPMS_UserMenuButton_CreateDate] DEFAULT (getdate()) FOR [CreateDate] GO
如今我處理權限就是經過這種中間表的形式來處理的,給用戶分配角色,而後再給角色分配權限。
權限表中記錄一下角色ID和要進行權限控制對象ID的對應關係。進行權限驗證的時候直接跟這個表來匹配。
這樣的話,按鈕權限、數據權限等等,均可以控制獲得。固然確定還會有其它更好的方法,之後再慢慢來完善。
個人權限控制是用了緩存,用戶登錄的時候根據用戶ID取得用戶的全部角色。
而後取用戶角色權限的合集加載到緩存中,進行權限判斷的時候直接跟緩存匹配。
取角色菜單SQL語句
SELECT M.MenuId, M.Code , M.FullName , M.Img , M.Category, M.Description, M.SortCode, M.ParentId, UM.MenuId AS IsExist FROM BPMS_SysMenu M LEFT JOIN BPMS_UserMenu UM ON M.MenuId = UM.MenuId AND UserId = @UserId ORDER BY M.SortCode
下面這段代碼是把權限讀入緩存
using DotNet.Kernel; using DotNet.Utilities; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace BPMS.Service { /// <summary> /// 使用服務器緩存 - 存儲權限 /// </summary> public class StorePermission { private readonly BPMS_PermissionDAL dal = new BPMS_PermissionDAL(); private static StorePermission item; public static StorePermission Instance { get { if (item == null) { item = new StorePermission(); } return item; } } /// <summary> /// 將【模塊權限】保存在服務器緩存中,提升性能。這樣就不用每次去數據庫讀 /// </summary> /// <param name="UserId">用戶主鍵</param> /// <param name="list"></param> /// <returns></returns> public void SetModulePermission(string UserId, IList list) { CacheHelper.Insert("Module" + UserId, list); } /// <summary> /// 將【操做按鈕權限】保存在服務器緩存中,提升性能。這樣就不用每次去數據庫讀 /// </summary> /// <param name="UserId">用戶主鍵</param> /// <param name="list"></param> /// <returns></returns> public void SetButtonPermission(string UserId, IList list) { CacheHelper.Insert("Button" + UserId, list); } /// <summary> /// 獲取【模塊權限】在服務器緩存中,提升性能。這樣就不用每次去數據庫讀 /// </summary> /// <param name="UserId">用戶主鍵</param> /// <param name="list"></param> /// <returns></returns> public object GetModulePermission(string UserId) { string strKey = "Module" + UserId; if (!CacheHelper.IsExist(strKey)) {this.SetModulePermission(UserId, dal.GetModulePermission(UserId)); } return CacheHelper.GetCache(strKey); } /// <summary> /// 獲取【操做按鈕權限】在服務器緩存中,提升性能。這樣就不用每次去數據庫讀 /// </summary> /// <param name="UserId">用戶主鍵</param> /// <param name="list"></param> /// <returns></returns> public object GetButtonPermission(string UserId) { string strKey = "Button" + UserId; if (!CacheHelper.IsExist(strKey)) { this.SetButtonPermission(UserId, dal.GetButtonPermission(UserId)); } return CacheHelper.GetCache(strKey); } } }
登錄成功後進入主界面,這裏作了兩步操做:
一、調用jajax獲取權限,將權限存入緩存。
二、根據角色對應的菜單數據動態拼接html,構造出一個個的<a href,點擊事後就能夠在右側的框架頁中打開。
三、這裏只會加載出用戶合法分配的菜單項,若是手動輸入頁面地址的話也會通過驗證。有效防止非法訪問。
載入權限及動態拼接菜單代碼:
var AccordionMenuJson = ""; function GetAccordionMenu() { var index = 0; var html = ""; getAjax("Frame.ashx", "action=LoadFirstMenu", function (data) { AccordionMenuJson = eval("(" + data + ")"); $.each(AccordionMenuJson, function (i) { if (AccordionMenuJson[i].ParentId == '9f8ce93a-fc2d-4914-a59c-a6b49494108f') { if (index == 0) { html += "<li><a style=\"border-top: 0px solid #ccc;\"><img src=\"/Themes/Images/32/" + AccordionMenuJson[i].Img + "\">" + AccordionMenuJson[i].FullName + "</a>"; } else { html += "<li><a><img src=\"/Themes/Images/32/" + AccordionMenuJson[i].Img + "\">" + AccordionMenuJson[i].FullName + "</a>"; } html += GetSubmenu(AccordionMenuJson[i].MenuId); html += "</li>"; index++; } }); }) $(".accordion").append(html); }
URL權限驗證:
這段代碼最好寫在頁面基類裏面,每一個頁面都去繼承它,任何URL申請都會拿去跟緩存中的權限去匹配一下。
非法請求就直接被拒絕。
#region URL權限驗證,增強安全驗證防止未受權匿名不合法的請求 /// <summary> /// URL權限驗證,增強安全驗證防止未受權匿名不合法的請求 /// </summary> public void IsUrlPermission() { bool IsOK = false; //獲取當前訪問頁面地址 string requestPath = RequestHelper.GetScriptName; string[] filterUrl = { "/Frame/HomeIndex.aspx", "/RMBase/SysUser/UpdateUserPwd.aspx" };//過濾特別頁面 for (int i = 0; i < filterUrl.Length; i++) { if (requestPath == filterUrl[i]) { IsOK = true; break; } } if (!IsOK) { string UserId = RequestSession.GetSessionUser().UserId; IList list = (IList)StorePermission.Instance.GetModulePermission(UserId); IList itemNode = IListHelper.IListToList<BPMS_ModulePermission>(list).FindAll(t => t.NavigateUrl == requestPath); if (itemNode.Count == 0) { StringBuilder strHTML = new StringBuilder(); strHTML.Append("<div><script type=\"text/javascript\">alert('很抱歉!您的權限不足,訪問被拒絕!')</script>"); strHTML.Append("</div>"); HttpContext.Current.Response.Write(strHTML.ToString()); HttpContext.Current.Response.End(); } } } #endregion
作到這裏我尚未作前臺控制權限的界面,手動從後臺去掉當前用戶的幾個菜單權限,果真顯示的菜單就少了:
這裏只將實現權限控制的主要代碼貼出來分享一下。說得可能不是很好,你們能夠去微軟高級php工程師的文章《大溼教我寫.net通用權限框架(1)之菜單導航篇》中有提供相關連接,但願本身動手也嘗試一下。
好東西確實值得好好去學習,之前我也是個單純的伸手黨,東西拿到手沒有好好研究。遇到問題也不知道怎麼解決,自已多動動手真的很好,駕馭的感受真不錯。
在這裏我很是感謝微軟高級php工程師和吉日嘎啦,本身第一次作權限框架,不少思想都是參考他們的,若是有想本身造輪子的能夠好好地去參考一下他們的博客。這裏就再也不提供源碼下載了,涉及做者的版權問題。須要的還請購買正版。呵呵。