asp.net mvc 全局權限過濾器及繼成權限方法


全局權限過濾器

//-----------------------------------------------------------------------
// <copyright file="PermissionFilter.cs" company="STO EXPRESS, Ltd.">
//     Copyright (c) 2015 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace DotNet.MVCInfrastructure.Filter
{
    using DotNet.Model;
    using DotNet.MVCInfrastructure.Attributes;
    using DotNet.MVCInfrastructure.Common;
    using DotNet.MVCInfrastructure.Enumerations;
    using DotNet.MVCInfrastructure.Models;
    using DotNet.Utilities;
    using DotNet.Business;

    /// <summary>
    /// 身份驗證過濾器
    /// 
    /// 1、匿名訪問
    /// 2、登錄就可以訪問
    /// 3、需要驗證是否有菜單或按鈕或資源的權限
    /// 
    /// 
    /// 修改紀錄
    /// 
    /// 2015-10-11 版本:1.0 SongBiao 創建文件。   
    /// 
    /// <author>
    ///     <name>SongBiao</name>
    ///     <date>2015-10-11</date>
    /// </author>
    /// </summary>
    public class CheckPermissionFilter : IAuthorizationFilter
    {
        /// <summary>
        /// 認證和授權是兩個方面
        /// </summary>
        /// <param name="filterContext"></param>
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            string pageUrl = filterContext.HttpContext.Request.Url == null ? "$$#$$" : filterContext.HttpContext.Request.Url.AbsolutePath; //OperateContext.GetThisPageUrl(false);

            //NLogHelper.Debug("CheckPermissionFilter:" + DateTime.Now + ",pageUrl=" + pageUrl);

            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            if (filterContext.HttpContext.Request.Url == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            // 是否是Ajax請求
            var bAjax = filterContext.HttpContext.Request.IsAjaxRequest();

            // 注意 所有允許匿名訪問的Controller,Action 都要設置匿名訪問標簽
            // 只對 Action 做判斷


            // 判斷 控制器 是否可以匿名訪問
            var controllerAnonymous = filterContext.Controller.GetType().GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
            // 先判斷控制器是否可以匿名訪問
            if ((controllerAnonymous != null && controllerAnonymous.Any()))
            {
                // 再檢查Action 是否有登錄檢查標簽
                var checkLoginActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
                if (checkLoginActionAttr != null && checkLoginActionAttr.Any())
                {
                }
                else
                {
                    // 如果沒有 讓他可以繼續訪問 因為把Controller 設置為 AllowAnonymous 了
                    return;
                }
                // 匿名就可以訪問 無需驗證登錄狀態
                //return;
            }

            // 判斷 Action 是否可以匿名訪問
            var actionAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
            if (actionAnonymous != null && actionAnonymous.Any())
            {
                // 匿名就可以訪問 無需驗證登錄狀態
                return;
            }

            // 1、允許匿名訪問 用於標記在授權期間要跳過 AuthorizeAttribute 的控制器和操作的特性 
            // 2017-03-06 檢查OpenId 只需傳來openId
            if (!string.IsNullOrWhiteSpace(filterContext.HttpContext.Request["openId"]))
            {
                string openId = filterContext.HttpContext.Request["openId"];
                UserLogOnResult userLogOnResult = Business.Utilities.LogOnByOpenId(openId, true);
                if (userLogOnResult != null && !string.IsNullOrEmpty(userLogOnResult.StatusCode) &&
                    userLogOnResult.StatusCode == Status.OK.ToString())
                {
                    // 用戶狀態存儲
                    OperateContext.Current.AddCurrent(userLogOnResult.UserInfo);
                }
            }
            else if (!string.IsNullOrWhiteSpace(filterContext.HttpContext.Request["AuthorizationCode"]))
            {
                // 2017-05-23 檢查傳過來的AuthorizationCode 支持code跳轉登錄
                string authorizationCode = filterContext.HttpContext.Request["AuthorizationCode"];
                string openId;
                if (BaseUserManager.VerifyAuthorizationCode(null, authorizationCode, out openId))
                {
                    // 用戶狀態存儲
                    UserLogOnResult userLogOnResult = Business.Utilities.LogOnByOpenId(openId, true);
                    if (userLogOnResult != null && !string.IsNullOrEmpty(userLogOnResult.StatusCode) &&
                        userLogOnResult.StatusCode == Status.OK.ToString())
                    {
                        // 用戶狀態存儲
                        OperateContext.Current.AddCurrent(userLogOnResult.UserInfo);
                    }
                }
            }

            // 2、判斷是否登錄或登錄已超時 需要重新登錄
            if (OperateContext.Current.UserInfo == null)
            {
                // 用戶狀態已過期
                // 判斷請求是否是Ajax請求
                if (bAjax)
                {
                    BusinessResultBase result = new BusinessResultBase();
                    result.Title = "未登錄或登錄已超時";
                    result.Status = false;
                    result.StatusCode = BusinessStatusCode.LoginTimeOut.ToString();
                    result.StatusMessage = "請重新登錄系統。";

                    var jsonResult = new JsonResult();
                    jsonResult.Data = result;
                    jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                    filterContext.Result = jsonResult;

                }
                else
                {
                    //filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area="",Controller = "Account", action = "Login" }));
                    filterContext.Result = new RedirectResult(BusinessSystemInfo.LoginUrl + "?returnUrl=" + pageUrl);
                }
            }
            else
            {
                // 用戶狀態未過期
                // 3、拒絕某個賬號登錄當前系統 判斷用戶是否在拒絕登錄的子系統中
                //if (OperateContext.Current.IsDenyVisit())
                //{
                //    if (bAjax)
                //    {
                //        BusinessResultBase result = new BusinessResultBase();
                //        result.Title = "拒絕訪問當前系統";
                //        result.Status = false;
                //        result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
                //        result.StatusMessage = "您的賬號不允許訪問當前系統。";
                //        var jsonResult = new JsonResult();
                //        jsonResult.Data = result;
                //        filterContext.Result = jsonResult;
                //    }
                //    else
                //    {
                //        filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = "Prompt", action = "DenyAccess", bAjaxReq = false, message = "沒有獲取您擁有的權限菜單,請嘗試重新登錄。" }));
                //    }
                //}
                //else 
                if (OperateContext.Current.UserInfo.IsAdministrator)
                {
                    // 超級管理員不檢查權限了
                    return;
                }
                else
                {
                    // 4、判斷用戶是否能夠訪問當前的controller,action

                    // 5、判斷登錄狀態 根據Controller或Action上的標簽 某些功能只需判斷是否登錄
                    // 判斷Controller上是否有CheckLoginAttribute標簽 只需要登錄就可以訪問

                    // 實際上檢查登錄也不好,應該檢查改Action是否是公開的  上面做為臨時用  某些系統沒有菜單或者沒有配置的Action可以這樣做  否則都必須配置菜單

                    // 判斷Controller上是否有CheckLoginAttribute標簽 只需要登錄就可以訪問的
                    var checkLoginControllerAttr = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
                    if (checkLoginControllerAttr != null && checkLoginControllerAttr.Any())
                    {
                        // 否則 沒有設置 CheckLogin 標簽的 就要驗證 向下執行  到這里 除了設置 CheckLogin 的action 都要檢查菜單訪問權限
                        return;
                    }

                    // 判斷action上是否有CheckLoginAttribute標簽 只需要登錄就可以訪問的
                    var checkLoginActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
                    if (checkLoginActionAttr != null && checkLoginActionAttr.Any())
                    {
                        return;
                    }

                    // 如果有 CheckActionPermission 就過 讓CheckActionPermission處理
                    var checkPermissionActionActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckActionPermissionAttribute), true) as IEnumerable<CheckActionPermissionAttribute>;
                    if (checkPermissionActionActionAttr != null && checkPermissionActionActionAttr.Any())
                    {
                        return;
                    }

                    // 6、有些要判斷是否有某個controller或 action的權限  通過獲取的權限菜單進行比較
                    // 用戶具有的菜單
                    var moduleList = OperateContext.Current.UserPermission;//.GetPermissionList(false);
                    if (moduleList == null || !moduleList.Any())
                    {
                        // 沒有獲取到任何菜單
                        if (bAjax)
                        {
                            BusinessResultBase result = new BusinessResultBase();
                            result.Title = "沒有訪問權限";
                            result.Status = false;
                            result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
                            result.StatusMessage = "沒有獲取到任何菜單,請嘗試重新登錄或咨詢系統開發人員。";
                            var jsonResult = new JsonResult();
                            jsonResult.Data = result;
                            jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                            filterContext.Result = jsonResult;
                            //var jsonResult = new JsonResult { Data = new BaseModels { IsError = true, ErrMsg = "請先登錄!", ErrCode = "unlogin" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
                            //filterContext.RequestContext.HttpContext.Response.Write(JsonConvert.SerializeObject(result));
                            //filterContext.RequestContext.HttpContext.Response.End();
                        }
                        else
                        {
                            //filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "沒有獲取您擁有的權限菜單,請嘗試重新登錄。" }));
                            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "沒有獲取到任何菜單,您沒有權限訪問當前內容:" + pageUrl + "" }));
                        }
                    }
                    else
                    {
                        // 獲取到菜單,進行比較
                        //var controllerName = (filterContext.RouteData.Values["controller"]).ToString().ToLower();
                        //var actionName = (filterContext.RouteData.Values["action"]).ToString().ToLower();
                        //var areaName = (filterContext.RouteData.DataTokens["area"] ?? "").ToString().ToLower();
                        /* 這個方式需要在controller或action上人為添加一個Attribute,沒有必要,直接可以取到當前的controller和actionName
                        // 用於標記在授權期間需要CustomerResourceAttribute 的操作的特性
                        var attNames = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CustomerResourceAttribute), true) as IEnumerable<CustomerResourceAttribute>;
                        // 判斷用戶的權限菜單中的code是否與控制器上標示的資源的code一致
                        var joinResult = (from aclEntity in moduleList
                                          join attName in attNames on aclEntity.Code equals attName.ResourceName
                                          select attName).Any();
                        if (!joinResult)
                        */
                        // 子系統菜單配置時,子系統的菜單code不能重復
                        // 同時支持通過訪問地址,Code來判斷
                        // 主要是要在全局過濾器里添加   filters.Add(new CheckPermissionFilter());

                        // 檢查地址是否與當前Action的地址一致
                        var modules = from module in moduleList
                                      where
                                      string.Equals(module.NavigateUrl, pageUrl, StringComparison.OrdinalIgnoreCase)
                                      //|| string.Equals(module.Code, controllerName, StringComparison.OrdinalIgnoreCase)
                                      //|| string.Equals(module.Code, actionName, StringComparison.OrdinalIgnoreCase)
                                      select module;
                        //string results = JsonHelper.SerializeObject(moduleList);
                        if (!modules.Any())
                        {
                            // 菜單沒有配置或者沒有權限
                            if (bAjax)
                            {
                                BusinessResultBase result = new BusinessResultBase();
                                result.Title = "沒有訪問權限";
                                result.Status = false;
                                result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
                                result.StatusMessage = "在您的權限中,您沒有權限訪問當前內容:" + pageUrl + "";
                                var jsonResult = new JsonResult();
                                jsonResult.Data = result;
                                jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                                filterContext.Result = jsonResult;
                            }
                            else
                            {
                                filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "在您的權限中,您沒有權限訪問當前內容:" + pageUrl + "" }));
                            }
                        }
                        else
                        {
                            return;
                        }
                    }
                }
            }
        }
    }
}

 

 

放在全局過濾器中即可,實現全部Action的訪問控制

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // filters.Add(new HandleErrorAttribute());
            filters.Add(new ElmahHandleErrorAttribute());
            // 全局身份驗證過濾器 更方便 
            filters.Add(new CheckPermissionFilter());
        }
    }

 

繼承權限的使用場景:

在某些情況下,只要具有其中一個Action的權限,那么跟他關聯的Action權限可以不用配置,
如在查詢場景中,配置了用戶訪問 Public ActionResult Index()主頁面的權限,查詢時請求的是 Public ActionResult List(......)
那么在List上加上標簽
[CheckActionPermission("/XXXArea/XXXController/Index")]
Public ActionResult List(......)
這樣就可以了,
CheckActionPermission里的參數是要繼續的Action的菜單路徑。
避免為List再配置菜單,授權。
另外象添加頁面,保存的action Save可以不必配置菜單授權,只要在上面配置[CheckActionPermission("/XXXArea/XXXController/Add")]即可。既然能看到添加界面,那么保存操作就應該是允許的。

//-----------------------------------------------------------------------
// <copyright file="CheckActionPermissionAttribute" company="STO, Ltd.">
//     Copyright (c) 2017 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Linq;
using System.Web.Mvc;

namespace DotNet.MVCInfrastructure.Attributes
{
    using DotNet.MVCInfrastructure.Enumerations;
    using DotNet.MVCInfrastructure.Models;
    using DotNet.MVCInfrastructure.Common;
    using DotNet.Utilities;

    /// <summary>
    /// CheckActionPermissionAttribute
    /// 記權限攔截
    /// 
    /// 修改紀錄
    /// 
    /// 2017-07-21 版本:1.0 SongBiao 創建文件。     
    /// 
    /// <author>
    ///     <name>SongBiao</name>
    ///     <date>2017-07-21</date>
    /// </author>
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class CheckActionPermissionAttribute : ActionFilterAttribute, IActionFilter
    {
        /// <summary>
        /// 檢查的Module
        /// </summary>
        private string Module;

        /// <summary>
        /// 構造函數 module表示檢查哪個菜單的權限即可
        /// 如某些列表,只需要檢查主頁面的菜單權限即可
        /// CheckActionPermission["/Headquarters/DaTouBiMain/Index"]
        /// </summary>
        /// <param name="module"></param>
        public CheckActionPermissionAttribute(string module)
        {
            Module = module;
        }

        FilterContextInfo fcinfo;
        /// <summary>
        /// 在執行操作方法之前由 ASP.NET MVC 框架調用。
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            string pageUrl = filterContext.HttpContext.Request.Url == null ? "$$#$$" : filterContext.HttpContext.Request.Url.AbsolutePath; //OperateContext.GetThisPageUrl(false);
            var bAjax = filterContext.HttpContext.Request.IsAjaxRequest();
            if (string.IsNullOrWhiteSpace(Module))
            {
                if (bAjax)
                {
                    BusinessResultBase result = new BusinessResultBase();
                    result.Title = "參數值不可以為空";
                    result.Status = false;
                    result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
                    result.StatusMessage = "CheckActionPermission參數值不可以為空。";
                    var jsonResult = new JsonResult();
                    jsonResult.Data = result;
                    jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                    filterContext.Result = jsonResult;
                }
                else
                {
                    filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "CheckActionPermission參數值不可以為空" }));
                }
            }
            else
            {
                if (OperateContext.Current.UserInfo == null)
                {
                    if (bAjax)
                    {
                        BusinessResultBase result = new BusinessResultBase();
                        result.Title = "未登錄或登錄已超時";
                        result.Status = false;
                        result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
                        result.StatusMessage = "未登錄或登錄已超時,請重新登錄系統。";
                        var jsonResult = new JsonResult();
                        jsonResult.Data = result;
                        jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                        filterContext.Result = jsonResult;
                    }
                    else
                    {
                        filterContext.Result = new RedirectResult(BusinessSystemInfo.LoginUrl + "?returnUrl=" + pageUrl);
                    }
                }
                else
                {
                    fcinfo = new FilterContextInfo(filterContext);
                    string areaName = fcinfo.AreaName;
                    string controllerName = fcinfo.ControllerName;
                    string actionName = fcinfo.ActionName;

                    // 當前訪問的
                    string currentModule = "/";
                    if (!string.IsNullOrWhiteSpace(areaName))
                    {
                        currentModule = currentModule + areaName;
                    }
                    if (!string.IsNullOrWhiteSpace(controllerName))
                    {
                        currentModule = currentModule + "/" + controllerName;
                    }
                    if (!string.IsNullOrWhiteSpace(actionName))
                    {
                        currentModule = currentModule + "/" + actionName;
                    }

                    var moduleList = OperateContext.Current.UserPermission;
                    // 比較Module在菜單中是否存在 如列表的權限只需要驗證主界面的權限即可
                    var modules = from module in moduleList
                                  where
                                  !string.IsNullOrWhiteSpace(module.NavigateUrl) && string.Equals(module.NavigateUrl.Trim(), Module.Trim(), StringComparison.OrdinalIgnoreCase)
                                  //|| string.Equals(module.Code, controllerName, StringComparison.OrdinalIgnoreCase)
                                  //|| string.Equals(module.Code, actionName, StringComparison.OrdinalIgnoreCase)
                                  select module;
                    //var modules = moduleList.Where(t => string.Equals(t.NavigateUrl, Module, StringComparison.OrdinalIgnoreCase));
                    if (modules == null || !modules.Any())
                    {
                        // 菜單沒有配置或者沒有權限
                        if (bAjax)
                        {
                            BusinessResultBase result = new BusinessResultBase();
                            result.Title = "沒有訪問權限";
                            result.Status = false;
                            result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
                            result.StatusMessage = "在您的權限中,您沒有權限訪問當前內容:" + currentModule + ",需要授予用戶對:" + Module + "的權限。";
                            var jsonResult = new JsonResult();
                            jsonResult.Data = result;
                            jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
                            filterContext.Result = jsonResult;
                        }
                        else
                        {
                            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "在您的權限中,您沒有權限訪問當前內容:" + currentModule + ",需要授予用戶對:" + Module + "的權限。" }));
                        }
                    }
                    else
                    {
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// 在執行操作方法后由 ASP.NET MVC 框架調用。
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {

            base.OnActionExecuted(filterContext);
        }

        /// <summary>
        ///  OnResultExecuted 在執行操作結果后由 ASP.NET MVC 框架調用。
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);
        }
        /// <summary>
        /// OnResultExecuting 在執行操作結果之前由 ASP.NET MVC 框架調用。
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);
        }

        public class FilterContextInfo
        {
            public FilterContextInfo(ActionExecutingContext filterContext)
            {
                #region 獲取鏈接中的字符
                DomainName = filterContext.HttpContext.Request.Url.Authority;
                AreaName = (filterContext.RouteData.DataTokens["area"] ?? "").ToString();
                ControllerName = filterContext.RouteData.Values["controller"].ToString();
                ActionName = filterContext.RouteData.Values["action"].ToString();
                #endregion
            }
            /// <summary>
            /// 獲取域名
            /// </summary>
            public string DomainName { get; set; }

            /// <summary>
            /// 獲取 controllerName 名稱
            /// </summary>
            public string AreaName { get; set; }

            /// <summary>
            /// 獲取 controllerName 名稱
            /// </summary>
            public string ControllerName { get; set; }

            /// <summary>
            /// 獲取ACTION 名稱
            /// </summary>
            public string ActionName { get; set; }
        }
    }
}

 


免責聲明!

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



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