1. 程式人生 > >基於MVC4+EasyUI的Web開發框架形成之旅--許可權控制

基於MVC4+EasyUI的Web開發框架形成之旅--許可權控制

我在上一篇隨筆《基於MVC4+EasyUI的Web開發框架形成之旅--框架總體介面介紹》中大概介紹了基於MVC的Web開發框架的許可權控制總體思路。其中的許可權控制就是分為“使用者登入身份驗證”、“控制器方法許可權控制”、“介面元素許可權控制”三種控制方式,可以為Web開發框架本身提供了很好使用者訪問控制和許可權控制,使得使用者介面呈現選單、Web介面的按鈕和內容、Action的提交控制,均能在總體許可權功能分配和控制之下。

本篇文章主要細化這三個方面的介紹,重點介紹“控制器方法許可權控制”、“介面元素許可權控制”這兩種許可權控制方式。

 1、使用者登入控制

登入介面如下所示。

其中登入的前臺頁面程式碼如下所示,其中可以在登入介面接收驗證碼(如果必要的話)。

        //實現使用者登入
        function LoginUserInfo() {
            //獲取單擊使用者登入按鈕的事件
            $("#btnLogin").click(function () {
                //首先獲取到要傳遞到控制器的引數,並且狗造成Json。UserName,UserPassword,Code
                var postData = {
                    UserName: $("#UserName").val(),
                    Password: $(
"#Password").val(), Code: $("#Code").val() }; //傳送非同步請求實現登入 ajax $.ajax({ url: '/Login/CheckUser', data: postData, cache: false, async: true, type:
'post', success: function (data) { if (data == "OK") { window.location.href = "/Home/Index"; } else { alert(data); window.location.href = "/Login/Index"; } } }); }); }

使用者登入的後臺控制器方法如下所示:

        /// <summary>
        /// 對使用者登入的操作進行驗證
        /// </summary>
        /// <param name="username">使用者賬號</param>
        /// <param name="password">使用者密碼</param>
        /// <param name="code">驗證碼</param>
        /// <returns></returns>
        public ActionResult CheckUser(string username, string password, string code)
        {
            string result = "";

            bool codeValidated = true;
            if (this.TempData["ValidateCode"] != null)
            {
                codeValidated = (this.TempData["ValidateCode"].ToString() == code);
            }

            if (string.IsNullOrEmpty(username))
            {
                result = "使用者名稱不能為空";
            }
            else if (!codeValidated)
            {
                result = "驗證碼輸入有誤";
            }
            else
            {
                string ip = GetClientIp();
                string macAddr = "";
                string identity = BLLFactory<WHC.Security.BLL.User>.Instance.VerifyUser(username, password, MyConstants.SystemType, ip, macAddr);
                if (!string.IsNullOrEmpty(identity))
                {
                    UserInfo info = BLLFactory<WHC.Security.BLL.User>.Instance.GetUserByName(username);
                    if (info != null)
                    {
                        result = "OK";
                        Session["UserInfo"] = info;
                        Session["Identity"] = info.Name.Trim();

                        #region 取得使用者的授權資訊,並存儲在Session中

                        List<FunctionInfo> functionList = BLLFactory<Function>.Instance.GetFunctionsByUser(info.ID, MyConstants.SystemType);
                        Dictionary<string, string> functionDict = new Dictionary<string, string>();
                        foreach (FunctionInfo functionInfo in functionList)
                        {
                            if (!string.IsNullOrEmpty(functionInfo.ControlID) &&
                                !functionDict.ContainsKey(functionInfo.ControlID))
                            {
                                functionDict.Add(functionInfo.ControlID, functionInfo.ControlID);
                            }
                        }
                        Session["Functions"] = functionDict;

                        #endregion
                    }
                }
                else
                {
                    result = "使用者名稱輸入錯誤或者您已經被禁用";
                }
            }

            return Content(result);
        }

從上面的程式碼,我們可以看到,在使用者登入成功後,後臺把使用者資訊、使用者許可權列表資訊放到了Session裡面,方便進行後面的許可權控制。

然後當前端頁面獲得成功響應並切換到Home的Index檢視前,後臺會呼叫Home的控制器,把一些使用者資訊放到了ViewBag物件裡面,並構造使用者的相關選單專案,程式碼如下所示。

    public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            if (CurrentUser != null)
            {
                ViewBag.FullName = CurrentUser.FullName;
                ViewBag.Name = CurrentUser.Name;

                StringBuilder sb = new StringBuilder();
                List<MenuInfo> menuList = BLLFactory<Menu>.Instance.GetTopMenu(MyConstants.SystemType);
                int i = 0;
                foreach (MenuInfo menuInfo in menuList)
                {
                    sb.Append(GetMenuItemString(menuInfo, i));
                    i++;
                }
                ViewBag.HeaderScript = sb.ToString();//一級選單程式碼
            }
            return View();            
        }

2、控制器方法許可權控制

 我們知道,對頁面的許可權控制,可以分為前端控制和後臺程式碼的控制,控制器方法的許可權控制屬於後臺程式碼的控制。為了方便基類程式碼的許可權控制,我們定義一個許可權控制鍵的類,用來記錄通用的增加、修改、刪除、檢視、列表、匯出等傳統控制元素,程式碼如下所示。

    /// <summary>
    /// 定義常用功能的控制ID,方便基類控制器對使用者許可權的控制
    /// </summary>
    [DataContract]
    [Serializable]
    public class AuthorizeKey
    {
        #region 常規功能控制ID
        /// <summary>
        /// 新增記錄的功能控制ID
        /// </summary>
        public string InsertKey { get; set; }

        /// <summary>
        /// 更新記錄的功能控制ID
        /// </summary>
        public string UpdateKey { get; set; }

        /// <summary>
        /// 刪除記錄的功能控制ID
        /// </summary>
        public string DeleteKey { get; set; }

        /// <summary>
        /// 檢視列表的功能控制ID
        /// </summary>
        public string ListKey { get; set; }

        /// <summary>
        /// 檢視明細的功能控制ID
        /// </summary>
        public string ViewKey { get; set; }

        /// <summary>
        /// 匯出記錄的功能控制ID
        /// </summary>
        public string ExportKey { get; set; } 
        #endregion

        #region 常規許可權判斷
        /// <summary>
        /// 判斷是否具有插入許可權
        /// </summary>
        public bool CanInsert { get; set; }

        /// <summary>
        /// 判斷是否具有更新許可權
        /// </summary>
        public bool CanUpdate { get; set; }

        /// <summary>
        /// 判斷是否具有刪除許可權
        /// </summary>
        public bool CanDelete { get; set; }

        /// <summary>
        /// 判斷是否具有列表許可權
        /// </summary>
        public bool CanList { get; set; }

        /// <summary>
        /// 判斷是否具有檢視許可權
        /// </summary>
        public bool CanView { get; set; }

        /// <summary>
        /// 判斷是否具有匯出許可權
        /// </summary>
        public bool CanExport { get; set; }

        #endregion

        /// <summary>
        /// 預設建構函式
        /// </summary>
        public AuthorizeKey() { }

        /// <summary>
        /// 常用建構函式
        /// </summary>
        public AuthorizeKey(string insert, string update, string delete, string view = "") 
        {
            this.InsertKey = insert;
            this.UpdateKey = update;
            this.DeleteKey = delete;
            this.ViewKey = view;
        }
    }

有了這個實體類,我們就可以在控制器的基類BaseController裡面實現一些控制邏輯了。首先我們在控制器每次執行方法前,都對許可權進行一個轉換,並把控制鍵儲存到ViewBage裡面,方便前端頁面的控制,如下程式碼所示。

        /// <summary>
        /// 重新基類在Action執行之前的事情
        /// </summary>
        /// <param name="filterContext">重寫方法的引數</param>
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);

            //得到使用者登入的資訊
            CurrentUser = Session["UserInfo"] as UserInfo;            
            if (CurrentUser == null)
            {
                Response.Redirect("/Login/Index");//如果使用者為空跳轉到登入介面
            }

            //設定授權屬性,然後賦值給ViewBag儲存
            ConvertAuthorizedInfo();
            ViewBag.AuthorizeKey = AuthorizeKey;
        }

其中ConvertAuthorizedInfo()函式是驗證登陸使用者是否具有相應的許可權的。

        /// <summary>
        /// 對AuthorizeKey物件裡面的操作許可權進行賦值,用於頁面判斷
        /// </summary>
        protected virtual void ConvertAuthorizedInfo()
        {
            //判斷使用者許可權
            AuthorizeKey.CanInsert = HasFunction(AuthorizeKey.InsertKey);
            AuthorizeKey.CanUpdate = HasFunction(AuthorizeKey.UpdateKey);
            AuthorizeKey.CanDelete = HasFunction(AuthorizeKey.DeleteKey);
            AuthorizeKey.CanView = HasFunction(AuthorizeKey.ViewKey);
            AuthorizeKey.CanList = HasFunction(AuthorizeKey.ListKey);
            AuthorizeKey.CanExport = HasFunction(AuthorizeKey.ExportKey);
        }

其中BaseController的控制器基類還定義了判斷使用者是否有某些許可權的邏輯,如果沒有沒有許可權,就會丟擲自定義異常(MyDenyAccessException),程式碼如下。

        /// <summary>
        /// 用於檢查方法執行前的許可權,如果未授權,返回MyDenyAccessException異常
        /// </summary>
        /// <param name="functionId"></param>
        protected virtual void CheckAuthorized(string functionId)
        {
            if(!HasFunction(functionId))
            {
                string errorMessage = "您未被授權使用該功能,請重新登入測試或聯絡管理員進行處理。";
                throw new MyDenyAccessException(errorMessage);
            }
        }

有了上面的這些邏輯,我們在業務控制器基類(BusinessController<B, T>)裡面,就可以實現對一些基本操作的API的許可權控制了。

    /// <summary>
    /// 本控制器基類專門為訪問資料業務物件而設的基類
    /// </summary>
    /// <typeparam name="B">業務物件型別</typeparam>
    /// <typeparam name="T">實體類型別</typeparam>
    public class BusinessController<B, T> : BaseController
        where B : class
        where T : WHC.Framework.ControlUtil.BaseEntity, new()
    {

        /// <summary>
        /// 插入指定物件到資料庫中
        /// </summary>
        /// <param name="info">指定的物件</param>
        /// <returns>執行操作是否成功。</returns>
        public virtual ActionResult Insert(T info)
        {
            //檢查使用者是否有許可權,否則丟擲MyDenyAccessException異常
            base.CheckAuthorized(AuthorizeKey.InsertKey);

            bool result = false;
            if (info != null)
            {
                result = baseBLL.Insert(info);
            }
            return Content(result);
        }

        /// <summary>
        /// 更新物件屬性到資料庫中
        /// </summary>
        /// <param name="info">指定的物件</param>
        /// <param name="id">主鍵ID的值</param>
        /// <returns>執行成功返回<c>true</c>,否則為<c>false</c></returns>
        public virtual ActionResult Update(string id, FormCollection formValues)
        {
            //檢查使用者是否有許可權,否則丟擲MyDenyAccessException異常
            base.CheckAuthorized(AuthorizeKey.UpdateKey);

            T obj = baseBLL.FindByID(id);
            if (obj != null)
            {
                //遍歷提交過來的資料(可能是實體類的部分屬性更新)
                foreach (string key in formValues.Keys)
                {
                    string value = formValues[key];
                    System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(key);
                    if (propertyInfo != null)
                    {
                        try
                        {
                            // obj物件有key的屬性,把對應的屬性值賦值給它(從字串轉換為合適的型別)
                            //如果轉換失敗,會丟擲InvalidCastException異常
                            propertyInfo.SetValue(obj, Convert.ChangeType(value, propertyInfo.PropertyType), null);
                        }
                        catch { }
                    }
                }
            }

            bool result = baseBLL.Update(obj, id);
            return Content(result);
        }

3、介面元素許可權控制

我們從上面那個Web開發框架的主介面圖可以看到,裡面對於某個特定的業務,增加、修改、、檢視、刪除等操作都放在了EasyUI的DataGrid工具欄裡面了,為了動態控制使用者能訪問的介面按鈕,我們需要結合使用者許可權集合進行介面呈現,首先我們把ToolBar放到一個層裡面進行定義,如下程式碼所示。

        //實現對DataGird控制元件的繫結操作
        function InitGrid(queryData) {
            $('#grid').datagrid({   //定位到Table標籤,Table標籤的ID是grid
                url: '/Information/FindWithPager',   //指向後臺的Action來獲取當前使用者的資訊的Json格式的資料
                title: '通知公告',
                iconCls: 'icon-view',
                height: 650,
                width: function () { return document.body.clientWidth * 0.9 },//自動寬度
                nowrap: true,
                autoRowHeight: true,
                striped: true,
                collapsible: true,
                pagination: true,
                pageSize: 50,
                pageList: [50, 100, 200],
                rownumbers: true,
                //sortName: 'ID',    //根據某個欄位給easyUI排序
                sortOrder: 'asc',
                remoteSort: false,
                idField: 'ID',
                queryParams: queryData,  //非同步查詢的引數
                columns: [[
                     { field: 'ck', checkbox: true },   //選擇
                     { title: '標題', field: 'Title', width: 350, sortable: true },
                     { title: '編輯者', field: 'Editor', width: 80, sortable: true },
                     { title: '編輯時間', field: 'EditTime', width: 150, sortable: true },
                     { title: '附件', field: 'Attachment_GUID', width: 250, sortable: true }
                ]],
                toolbar: "#gridtoolbar",

然後在HTML裡面新增gridtoolbar的層定義,作為easyUI的表格控制元件的工具條。由於使用了HTML輔助類來實現介面控制元件程式碼控制生成,因此已經可以達到了介面許可權的控制了。使用這種HTML層定義的工具條定義方式,比通過指令碼定義的工具條效果少了一個分隔線,其他的都還是一致的。

    <div id="gridtoolbar" style="padding: 5px; height: auto">
        <div style="margin-bottom: 5px">
            @if (@ViewBag.AuthorizeKey.CanInsert)
            {
                @Html.ActionLink("新增", null, null, new {onclick="ShowAddDialog()", data_options="iconCls:'icon-add', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"})
            }
            @if (@ViewBag.AuthorizeKey.CanUpdate)
            {
                @Html.ActionLink("修改", null, null, new {onclick="ShowEditOrViewDialog()", data_options="iconCls:'icon-edit', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"})
            }
            @if (@ViewBag.AuthorizeKey.CanDelete)
            {
                @Html.ActionLink("刪除", null, null, new {onclick="Delete()", data_options="iconCls:'icon-remove', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"})
            }
            @if (@Html.HasFunction("Information/View"))
            {
                @Html.ActionLink("檢視", null, null, new {onclick="ShowEditOrViewDialog('view')", data_options="iconCls:'icon-table', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"})
            }
            @Html.ActionLink("重新整理", null, null, new {onclick="$('#grid').datagrid('reload');", data_options="iconCls:'icon-reload', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"}) 
                      
        </div>
    </div>

上面使用了兩種方式來判斷使用者的許可權的,一種是使用這種ViewBag物件的樹形進行判斷,如下所示。

@if (@ViewBag.AuthorizeKey.CanDelete)

還有一種是使用HTML輔助類的擴充套件方法進行判斷,這種方法適用於一些非常規的許可權控制集合的判斷,如下所示

@if (@Html.HasFunction("Information/View"))

其中HTML輔助類方法是通過擴充套件靜態方法進行實現,程式碼如下所示。

    public static class HtmlHelpers
    {
        public static bool HasFunction(this HtmlHelper helper, string functionId)
        {
            return Permission.HasFunction(functionId);
        }

        public static bool IsAdmin()
        {
            return Permission.IsAdmin();
        }
    }

上面的介面控制方法,是通過控制介面程式碼的生成與否進行許可權控制的,前面我們講了,通過後臺程式碼的控制器方法也是可以實現控制,而且是丟擲自定義的錯誤,那麼我們在使用Ajax方法呼叫的時候,也可以對這個錯誤資訊進行友好顯示,提示使用者許可權不足,前端頁面操作程式碼如下。

        //繫結新增按鈕的事件
        function BindAddEvent() {
            $("#btnAddOK").click(function () {
                //判斷表單的資訊是否通過驗證
                var validate = $("#ffAdd").form('validate');
                if (validate == false) {
                    return false;
                }
                
                var postData = $("#ffAdd").serializeArray();
                $.post("/Information/Insert", postData, function (data) {
                    if (data = "true") {
                        //新增成功  1.關閉彈出層,2.重新整理DataGird
                        $.messager.alert("提示", "新增成功");
                        $("#DivAdd").dialog("close");
                        $("#grid").datagrid("reload");
                        $("#ffAdd").form("clear");

                        //本頁面的型別為【通知公告】,固定不變
                        $("#Category").val("通知公告");
                    }
                    else {
                        $.messager.alert("提示", "新增失敗,請您檢查");
                    }
                }).error(function () {
                    $.messager.alert("提示", "您未被授權使用該功能,請聯絡管理員進行處理。", 'warning');
                });
            });
        }

 以上就是我對Web開發框架中的許可權控制幾個方面的思路和程式碼,希望拋磚引玉,獲得大家更好的反饋和支援。

基於MVC4+EasyUI的Web開發框架的系列文章:

相關推薦

基於MVC4+EasyUI的Web開發框架形成--許可權控制

我在上一篇隨筆《基於MVC4+EasyUI的Web開發框架形成之旅--框架總體介面介紹》中大概介紹了基於MVC的Web開發框架的許可權控制總體思路。其中的許可權控制就是分為“使用者登入身份驗證”、“控制器方法許可權控制”、“介面元素許可權控制”三種控制方式,可以為Web開發框架本身提供了很好使用者訪問控制和許

基於MVC4+EasyUI的Web開發框架形成--介面控制元件的使用

在前面介紹了兩篇關於我的基於MVC4+EasyUI技術的Web開發框架的隨筆,本篇繼續介紹其中介面部分的一些使用知識,包括控制元件的賦值、取值、清空,以及相關的使用。 我們知道,一般Web介面包括的介面控制元件有:單行文字框、多行文字框、密碼文字框、下拉列表Combobox、日期輸入控制元件、數值輸入控制元

(轉)基於MVC4+EasyUI的Web開發框架形成--MVC控制器的設計

cli dex txt strip -1 function 特殊 remote 文章 http://www.cnblogs.com/wuhuacong/p/3284628.html 自從上篇《基於MVC4+EasyUI的Web開發框架形成之旅--總體介紹》總體性的概括,得

基於MVC4+EasyUI的Web開發框架形成--附件上傳元件uploadify的使用

大概一年前,我還在用Asp.NET開發一些行業管理系統的時候,就曾經使用這個元件作為檔案的上傳操作,在隨筆《Web開發中的檔案上傳元件uploadify的使用》中可以看到,Asp.NET中如何使用這個元件進行檔案上傳的,當時上傳檔案的處理主要也是使用ashx一般處理程式來進行處理的。本文主要介紹我的Web開發

基於MVC4+EasyUI的Web開發框架形成--總體介紹

最近花了很多時間在重構和進一步提煉Winform開發框架的工作上,加上時不時有一些專案的開發工作,我部落格裡面介紹Web開發框架的文章比較少,其實以前在單位工作,80%的時間是做Web開發的,很早就形成了自己的一套Web開發框架,但是由於一些個人原因,一直沒有來得及好好整理和推廣,其實那套Web開發框架對大多

基於MVC4+EasyUI的Web開發框架形成--基類控制器CRUD的操作

在上一篇隨筆中,我對Web開發框架的總體介面進行了介紹,其中並提到了我的《Web開發框架》的控制器的設計關係,Web開發框架沿用了我的《Winform開發框架》的很多架構設計思路和特點,對Controller進行了封裝。使得控制器能夠獲得很好的繼承關係,並能以更少的程式碼,更高效的開發效率,實現Web專案的開

基於MVC4+EasyUI的Web開發框架形成--MVC控制器的設計

自從上篇《基於MVC4+EasyUI的Web開發框架形成之旅--總體介紹》總體性的概括,得到很多同行的關注和支援,不過上一篇主要是介紹一個總體的介面效果和思路,本系列的文章將逐步介紹其中的細節,本文主要介紹整個Web開發框架中的MVC控制器的設計。在設計之初,我就希望儘可能的減少程式碼,提高程式設計模型的統一

基於MVC4+EasyUI的Web開發框架形成--框架總體介面介紹

在前面介紹了一些關於最新基於MVC4+EasyUI的Web開發框架文章,雖然Web開發框架的相關技術文章會隨著技術的探討一直寫下去,不過這個系列的文章,到這裡做一個總結,展示一下整體基於MVC4+EasyUI的介面效果,讓大家對這款Web開發框架有一個形象的瞭解,介面設計以及相關思路可以借鑑提高,也可以對相關

Entity Framework 實體框架形成--基於泛型的倉儲模式的實體框架(1)

一些讀者也經常問問一些問題,不過最近我確實也很忙,除了處理日常工作外,平常主要的時間也花在了繼續研究微軟的實體框架(EntityFramework)方面了。這個實體框架加入了很多特性(例如LINQ等),目前也已經應用的比較成熟了,之所以一直沒有整理成一個符合自己開發模式的

前端開發框架總結Angular實用技巧(六)

                                    前端開發框架總結之Angular實用技巧(六)

前端開發框架總結Angular實用技巧(五)

                          前端開發框架總結之Angular實用技巧(五) 上文講了Angular中網路請求相關的知識,掌握了這些,我們就可以

前端開發框架總結Angular實用技巧(四)

                       前端開發框架總結之Angular實用技巧(四) 上文講了Angular中路由的相關的知識,掌握了這些,我們就可以構建比較複雜的頁面

前端開發框架總結Angular實用技巧(三)

                             前端開發框架總結之Angular實用技巧(三) 上文講了Angular中頁面重新整理和資

前端開發框架總結Angular實用技巧(二)

                                前端開發框架總結之Angular實用技巧(二) 上文講了Angular自

前端開發框架總結Angular實用技巧(一)

                              前端開發框架總結之Angular實用技巧(一) 前言: 前一段時間接觸了Angul

前端開發框架總結利用Jtopo實現網路拓撲功能(四)

                     前端開發框架總結之利用Jtopo實現網路拓撲功能(四) 上文我們講了拓撲容器相關的互動設計和實現思路以及一些關鍵技術細節。至此,我們已經覆蓋了結

前端開發框架總結利用Jtopo實現網路拓撲功能(三)

                     前端開發框架總結之利用Jtopo實現網路拓撲功能(三) 上文我們講了一些拓撲連線、拓撲文字節點相關的互動設計和實現思路以及一些關鍵技術細節。本文

前端開發框架總結利用Jtopo實現網路拓撲功能(二)

                    前端開發框架總結之利用Jtopo實現網路拓撲功能(二) 上文我們講了一些拓撲結點生成的實際場景設計和實現思路以及一些關鍵技術細節。本文我們繼續我們的拓撲管理

前端開發框架總結利用Jtopo實現網路拓撲功能(一)

                      前端開發框架總結之利用Jtopo實現網路拓撲功能(一) 前言:     前段時間由於專案需要實現一個網路裝置拓撲管理的

springboot全能框架學習

springboot隨著動態語言的發展,java需要獨立執行的基於spring框架內的搭建工程工具。 1.獨立執行spring專案。不需要tomcat 2.內嵌servlet容器。外部只有jdk 3.提供starter'簡化maven依賴配置。 4.自動配置spring。本質上是包掃描 通俗講就是搭