在系統開發的時候一個老生常談的許可權管理問題,翻閱了很多的網路資料,但是總感覺離實際使用還有一段距離,其實許可權控制無非就幾個“請求、頁面按鈕、欄位顯示”, 對於前端許可權就需要配合JS了, 這裡主要展示我這些年來一直使用的一個關於許可權管理的案例,如果你只是寫一個簡單的CMS系統,那麼下麵代碼其實拷貝就可 ...
在系統開發的時候一個老生常談的許可權管理問題,翻閱了很多的網路資料,但是總感覺離實際使用還有一段距離,其實許可權控制無非就幾個“請求、頁面按鈕、欄位顯示”, 對於前端許可權就需要配合JS了, 這裡主要展示我這些年來一直使用的一個關於許可權管理的案例,如果你只是寫一個簡單的CMS系統,那麼下麵代碼其實拷貝就可以用了,如果是需要進行較大型系統開發就需要進一步封裝, 下麵是我的思路,寫的不好大家勿噴哈,有不同意見的留下一起探討。
1、在MVC開發時路由中包含了我們所有請求信息(Controller、Action、URL全文),而MVC又提供了過濾器的機制,因此我們將許可權管理封裝到過濾其中。首先把每個Controller的繼承封裝一個BaseController,這個Controller基類可以編寫一些直接調用的方法,例如Session等。
/// <summary> /// 系統基礎控制器類 /// </summary> public abstract class BaseUIController : Controller { /// <summary> /// 頁面初始化過程 /// </summary> protected override void Initialize(System.Web.Routing.RequestContext requestContext) { requestContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); base.Initialize(requestContext); } /// <summary> /// 內容返回 /// </summary> /// <param name="content">特定內容供JS參考是否含流程定義等動作。</param> /// <param name="contentType"></param> /// <param name="contentEncoding"></param> /// <returns></returns> protected override ContentResult Content(string content, string contentType, Encoding contentEncoding) { return base.Content(content, contentType, contentEncoding); } /// <summary> /// 登陸用戶ID (Session存儲) /// </summary> public long LoginedUserID { get { object UID = Ant.UI.Controls.SessionConfig.GetSession("UID"); if (Session == null || UID == null) return -1; else { return (long)UID; } } set { Ant.UI.Controls.SessionConfig.SetSession("UID", value); } } /// <summary> /// 獲取URL參數值,返回 NULL 或 值 /// </summary> /// <param name="keyName">參數名</param> public string RequestUrlParams(string keyName) { if (Array.IndexOf(Request.QueryString.AllKeys, keyName) >= 0) return Request.QueryString[keyName].ToString(); else return null; } }
2、這個時候我們編寫一個過濾器,在這個過濾器中可以在請求的Controller中獲取controller名稱和action名稱。 有了這兩個名稱就能定位到具體的頁面了,當然在得到頁面之前我們可以判斷用戶是否已經登錄。而頁面許可權頁已經用資料庫保存起來:A用戶或R角色對某一個Controller/Action的頁面(這個頁面可能是新增信息、修改、刪除信息的請求鏈接)是否有訪問許可權。
/// <summary> /// 用戶登錄過濾器 /// </summary> public class UserLoginedFilter : AuthorizeAttribute { public string name { get; set; } /// <summary> /// 頁面授權過程(驗證用戶登錄狀態) /// </summary> /// <param name="filterContext"></param> public override void OnAuthorization(AuthorizationContext filterContext) { UI.Controllers.BaseUIController _ControllerObj = (UI.Controllers.BaseUIController)filterContext.Controller; string controllerStr = _ControllerObj.ControllerContext.RouteData.Values["controller"].ToString(); string actionStr = _ControllerObj.ControllerContext.RouteData.Values["action"].ToString(); //用戶尚未登陸 或 登陸超時。 if (_ControllerObj.LoginedUserID == -1) { UserError.LoginError(); } // 許可權驗證 根據 Controller 與 Action(這裡可以將許可權緩存取出來進行用戶匹配,如果被拒絕可以拋出異常) else if ( controllerStr.ToLower() == "admin" && actionStr.ToLower() == "login") { throw new HttpException("許可權驗證。" + _ControllerObj.ControllerContext.Controller.ToString()); } base.OnAuthorization(filterContext); } }
3、此時頁面調用時就不用在每個Action中編寫許可權驗證代碼啦(有些許可權驗證的源碼會在Action上增加過濾器,為了偷懶硬是想方設法把許可權封裝成直接在Controller類上添加一次過濾器就能完成的實現)
[UserLoginedFilter] public class AdminController : UI.Controllers.BaseUIController { /// <summary> /// 登陸(登陸頁面不需要許可權驗證,增加AllowAnonymous特性) /// </summary> [AllowAnonymous] public ActionResult Login(FormCollection form) { return View(); } /// <summary> /// 主界面 /// </summary> public ActionResult Index() { return View(); } }
上面只是我在系統開發過程中對於許可權管理的一個思路,也實實在在的應用到了一些系統中,當然上面這些代碼只能勉強判斷是否登陸,如果你需要對數據許可權、頁面許可權、欄位許可權進行管理那麼就需要進一步的擴展。如果需要在拋出異常後將異常信息友好的返回給客戶端,那你仍然需要在Global中捕獲返回狀態碼以及錯誤信息。因此我說這隻是我的一個思路,路過的朋友給我建議。多謝!