回到目錄 對於一個後臺管理系統來說,你的許可權設計與安全是重中之重,當你為一個許可權分配一些菜單後,當這個許可權的用戶沒有菜單許可權時,這個菜單的URL是不可以被用戶訪問的,而在之前的設計中,沒有考慮到這點,所以本次Lind.DDD.Manager的升級中,需要把這塊完善一下,將會在8月的Lind.DDD中 ...
對於一個後臺管理系統來說,你的許可權設計與安全是重中之重,當你為一個許可權分配一些菜單後,當這個許可權的用戶沒有菜單許可權時,這個菜單的URL是不可以被用戶訪問的,而在之前的設計中,沒有考慮到這點,所以本次Lind.DDD.Manager的升級中,需要把這塊完善一下,將會在8月的Lind.DDD中奉獻給大家,敬請期待!
思路
用戶訪問
==>
mvc根據url找到controller/action
==>
判斷這個URL是否為庫中定義的URL(排除非正常URL,PartialView產生的URL)
==>
判斷用戶是否有這個URL的許可權
==>
正常訪問
層關係圖
實現
使用了MVC環境下的AOP方法攔截技術,它主要通過過濾器(AuthorizeAttribute)來實現對action的攔截,然後註入自己的代碼,這也是MVC幾大過濾器帶給我們的驚喜!
AuthorizeAttribute 為我們提供了一個用戶授權的過濾器,當用戶訪問Action之前,它將被執行
// 摘要: // 表示一個特性,該特性用於限制調用方對操作方法的訪問。 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter { // 摘要: // 初始化 System.Web.Mvc.AuthorizeAttribute 類的新實例。 public AuthorizeAttribute(); // 摘要: // 獲取或設置用戶角色。 // // 返回結果: // 用戶角色。 public string Roles { get; set; } // // 摘要: // 獲取此特性的唯一標識符。 // // 返回結果: // 此特性的唯一標識符。 public override object TypeId { get; } // // 摘要: // 獲取或設置授權用戶。 // // 返回結果: // 授權用戶。 public string Users { get; set; } // 摘要: // 重寫時,提供一個入口點用於進行自定義授權檢查。 // // 參數: // httpContext: // HTTP 上下文,它封裝有關單個 HTTP 請求的所有 HTTP 特定的信息。 // // 返回結果: // 如果用戶已經過授權,則為 true;否則為 false。 // // 異常: // System.ArgumentNullException: // httpContext 參數為 null。 protected virtual bool AuthorizeCore(HttpContextBase httpContext); // // 摘要: // 處理未能授權的 HTTP 請求。 // // 參數: // filterContext: // 封裝有關使用 System.Web.Mvc.AuthorizeAttribute 的信息。filterContext 對象包括控制器、HTTP 上下文、請求上下文、操作結果和路由數據。 protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext); // // 摘要: // 在過程請求授權時調用。 // // 參數: // filterContext: // 篩選器上下文,它封裝有關使用 System.Web.Mvc.AuthorizeAttribute 的信息。 // // 異常: // System.ArgumentNullException: // filterContext 參數為 null。 public virtual void OnAuthorization(AuthorizationContext filterContext); // // 摘要: // 在緩存模塊請求授權時調用。 // // 參數: // httpContext: // HTTP 上下文,它封裝有關單個 HTTP 請求的所有 HTTP 特定的信息。 // // 返回結果: // 對驗證狀態的引用。 // // 異常: // System.ArgumentNullException: // httpContext 參數為 null。 protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext); }
對於我們的菜單許可權過濾器,需要繼承它,我們起名為ManagerUrlAttribute,下麵是大叔設計的代碼,大家可以作為參考
/// <summary> /// 後臺URL菜單的許可權 /// 需要考慮到PartialView的問題 /// </summary> public class ManagerUrlAttribute : AuthorizeAttribute { /// <summary> /// 驗證失敗後所指向的控制器和action /// 可以在使用特性時為它進行賦值 /// </summary> public ManagerUrlAttribute(string failControllerName = "Home", string failActionName = "Login") { _failControllerName = failControllerName; _failActionName = failActionName; } /// <summary> /// 出錯時要跳轉的控制器 /// </summary> string _failControllerName; /// <summary> /// 出錯時要跳轉的action /// </summary> string _failActionName; /// <summary> /// 菜單倉儲 /// </summary> static IExtensionRepository<WebManageMenus> menuRepository = new ManagerEfRepository<WebManageMenus>(new ManagerContext()); /// <summary> /// 所有已經定義的菜單項 /// </summary> static List<WebManageMenus> allMenuList = menuRepository.GetModel().ToList(); public override void OnAuthorization(AuthorizationContext filterContext) { var menuIdArr = Array.ConvertAll<string, int>(CurrentUser.ExtInfo.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries), i => int.Parse(i)); var menuUrlArr = allMenuList.Where(i => menuIdArr.Contains(i.Id)).Select(i => i.LinkUrl).ToList(); var controllerName = filterContext.RouteData.Values["controller"].ToString(); var actionName = filterContext.RouteData.Values["action"].ToString(); var isValid = allMenuList.FirstOrDefault(i => i.LinkUrl == "/" + controllerName + "/" + actionName) != null;//是否為有效的URL,過濾分佈視圖 //當前為正常頁面,不是分佈視圖 if (isValid) { //沒有當前URL的許可權,跳到登陸頁 if (!menuUrlArr.Contains("/" + controllerName + "/" + actionName)) { filterContext.Result = new RedirectToRouteResult("Default", new RouteValueDictionary { { "Action",_failActionName }, { "Controller", _failControllerName} }); } } } }
本代碼解決了分佈視圖在過濾器中的尷尬,將分佈視圖產生的action進行過濾,我們先將所有定義的菜單URL取出來,然後用戶訪問時,先判斷當前URL是否為已經定義的URL,如果是,再進行許可權的比較.