1. 程式人生 > >NetCore MVC 許可權設計

NetCore MVC 許可權設計

1.以前我們的許可權主要靠手工錄入到系統中,然後進行驗證,這種方式不僅耗時,而且一旦按鈕id 發生變動等情況 維護比較麻煩,現在我們採用直接從Controller中讀取對應的action 進行設定許可權,這樣就不需要做過多維護以下是原始碼

 /// <summary>
    /// 控制器操作類 
    /// </summary>
    public class ControllerHelper
    {
        private const string All_ControllerActionCacheKey = "All_System_ControllerHelper_Actions_List";

        /// <summary>
        /// 獲取所有頁面的Action
        /// </summary>
        public static List<BasePermissionAttribute> AuthAttributes
        {
            get
            {
                return ApplicationEnvironments.CacheService.Get<List<BasePermissionAttribute>>(All_ControllerActionCacheKey, () =>
                {
                    return new ControllerHelper().GetAllActions();
                }, true);

            }
        }
       
        /// <summary>
        /// 獲取單頁面的Action
        /// </summary>
        /// <param name="areaName"></param>
        /// <param name="controllerName"></param>
        /// <returns></returns>
        public static List<BasePermissionAttribute> GetActionsByPage(string areaName,string controllerName) {
            
            if (AuthAttributes != null && AuthAttributes.Count > 0)
            {
                return AuthAttributes.Where(x=>x.AreaName.ToLower().Equals(areaName.ToLower())&&x.ControllerName.ToLower().Equals(controllerName.ToLower())).ToList();
            }
            return null;
        }

        /// <summary>
        /// 獲取所有頁面的Action
        /// </summary>
        private List<BasePermissionAttribute> GetAllActions()
        {
            #region 通過反射讀取Action方法寫入對應許可權至集合
            List<BasePermissionAttribute> authAttributes = new List<BasePermissionAttribute>();
            //讀取當前專案程式集中整合自AdminController的控制器
            var files = System.IO.Directory.GetFiles(AppContext.BaseDirectory, "*.Web.dll");
            if (files != null && files.Length > 0)
            {
                foreach (var file in files)
                {
                    var assembly = Assembly.LoadFrom(file);
                    var types = assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(BaseController))).ToList();
                  
                    //var now = DateTime.Now;

                    foreach (var type in types)
                    {
                        //獲取所有action方法
                        GetControllerActions(type,ref authAttributes);
                    }
                }
            }
            return authAttributes;
            #endregion
        }

        /// <summary>
        /// 獲取單個控制器中的Actions
        /// </summary>
        /// <param name="controller"></param>
        private void GetControllerActions(Type controller, ref List<BasePermissionAttribute> authAttributes)
        {
            var areaAttr= controller.GetCustomAttribute(typeof(AreaAttribute),true);
            string areaName = "";
            if (areaAttr != null)
            {
                areaName = (areaAttr as AreaAttribute).RouteValue;
            }
            var members = controller.GetMethods().Where(e => e.ReturnType.Name == nameof(ActionResult)
                       || e.ReturnType.Name == nameof(IActionResult)
                       || e.ReturnType.Name == nameof(JsonResult)
                       );
            string[] systemAction = {"index","forgrid" };
            foreach (var member in members)
            {
                if (systemAction.Contains(member.Name.ToLower()))
                {
                    continue;
                }
                //獲取功能列表
                var attr = member.GetCustomAttribute(typeof(BasePermissionAttribute), true) ;
                if (attr == null)
                    continue;
                var auth = attr as BasePermissionAttribute;
                if (string.IsNullOrWhiteSpace(auth.ActionCode)|| !string.IsNullOrWhiteSpace(auth.ActionName) || auth.IsParent|| auth.NoAccess)
                {
                    continue;
                }
                auth.AreaName = areaName;
                if (string.IsNullOrWhiteSpace(auth.ActionName))
                {
                    auth.ActionName = member.Name;
                }
                auth.ControllerName = controller.Name.Replace("Controller", "");
                //功能對應的二級選單
                authAttributes.Add(auth);
            }
            
        }
    }

 

2.BasePermissionAttribute許可權基類,

主要用於定義action 訪問屬性,由於在底層需要引用該類方便儲存讀取controller中的action 所以需要這個基類,如果專案這個類放在業務層是不需要分開的

 

public class BasePermissionAttribute :Attribute, IAsyncAuthorizationFilter 
    {
        /// <summary>
        /// get請求是否需要驗證許可權 預設是
        /// </summary>
        public bool IsGet { get; set; }
        /// <summary>
        /// post請求是否需要驗證許可權 預設是
        /// </summary>
        public bool IsPost { get; set; }

        /// <summary>
        /// 描述
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// 與其它ActionName許可權一樣
        /// </summary>
        public string ActionCode { get; set; }

        /// <summary>
        /// Action名稱
        /// </summary>
        public string ActionName { get; set; }

        /// <summary>
        /// 域名稱
        /// </summary>
        public string AreaName { get; set; }
        /// <summary>
        /// 控制器名稱
        /// </summary>

        public string ControllerName { get; set; }

        /// <summary>
        /// 是否繼承controller  Index訪問許可權
        /// </summary>
        public bool IsParent { get; set; }

        /// <summary>
        /// 不允許訪問
        /// </summary>
        public bool NoAccess { get; set; }
        //public PermissionAttribute()
        //{
        //    IsGet = true;
        //    IsPost = true;
        //}
        public BasePermissionAttribute() {
            IsGet = true;
            IsPost = true;
            IsParent = false;
        }
        public BasePermissionAttribute(string code,string description)
        {
            IsGet = true;
            IsPost = true;
            IsParent = false;
            ActionCode = "BTN" + code;
            Description = description;
        }
       
        /// <summary>
        /// 輸出錯誤資訊
        /// </summary>
        /// <param name="filterContext"></param>
        /// <param name="strError"></param>
        public void WriteResult(AuthorizationFilterContext filterContext,  string strError)
        {
            var areaName = "";
            var actionName = filterContext.RouteData.Values["Action"].ToString();
            var controllerName = filterContext.RouteData.Values["controller"].ToString();
            if (filterContext.RouteData.DataTokens["area"] != null)
            {
                areaName = filterContext.RouteData.DataTokens["area"].ToString();
            }
            //new LogErrorService().Save((!string.IsNullOrEmpty(areaName)?areaName+"/"+controllerName:controllerName),actionName,strError,strError);
            if (AppHttpContext.IsPost&&AppHttpContext.IsAjax)
            {
                filterContext.HttpContext.Response.StatusCode = 200;
                filterContext.Result = new JsonResult(AjaxResult.Error(strError, -100));
            }
            else
            {
                var view = new ViewResult();
                view.ViewName = "~/Views/Home/Error.cshtml";
                view.ViewData = new Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary(new BaseController().ViewData);
                view.ViewData["Message"] = strError;
                view.ViewData["Exception"] = new Exception(strError);
                filterContext.Result = view;

            }

        }

        public virtual Task OnAuthorizationAsync(AuthorizationFilterContext filterContext)
        {
            return Task.CompletedTask;
        }

3.許可權的具體實現

public class PermissionAttribute : BasePermissionAttribute
    {
        public PermissionAttribute():base()
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code">許可權標識(controller中請勿重複)</param>
        /// <param name="description">描述</param>
        public PermissionAttribute(string actionCode, string description) : base(actionCode, description)
        {
        }
        public override Task OnAuthorizationAsync(AuthorizationFilterContext filterContext)
        {
            //匿名標識 無需驗證
            if (filterContext.Filters.Any(e => (e as AllowAnonymousAttribute) != null))
                return Task.CompletedTask;
            if ((AppHttpContext.IsPost && !IsPost)||(!AppHttpContext.IsPost && !IsGet))
            {
                return Task.CompletedTask;
            }
            if (NoAccess)
            {
                WriteResult(filterContext, "該介面不允許訪問");
                return Task.CompletedTask;
            }
            
            if (!AppHttpContext.Current.User.Identity.IsAuthenticated)
            {
                WriteResult(filterContext, "未登入,無權訪問");
                return Task.CompletedTask;
            }
            var userEntity = ApplicationEnvironments.DefaultSession.GetUser<UserEntity>();
            if (userEntity == null)
            {
                WriteResult(filterContext, "對不起,您無權訪問");
                return Task.CompletedTask;
            }
            //獲取請求的區域,控制器,action名稱
            this.AreaName = string.IsNullOrWhiteSpace(this.AreaName) ? filterContext.RouteData.Values["area"]?.ToString() : this.AreaName;
            this.ControllerName = string.IsNullOrWhiteSpace(this.ControllerName) ? filterContext.RouteData.Values["controller"]?.ToString() : this.ControllerName;
            this.ActionName = string.IsNullOrWhiteSpace(this.ActionName) ? filterContext.RouteData.Values["action"]?.ToString() : this.ActionName;
            if (IsParent)
            {
                this.ActionName = "Index";
            }
            var isPermit = false;
            if (string.IsNullOrWhiteSpace(ControllerName) || ControllerName.ToLower().Equals("home"))
            {
                ControllerName = "";
            }
            if (string.IsNullOrWhiteSpace(ActionName) || ActionName.ToLower().Equals("index"))
            {
                ActionName = "";
            }
            string routeUrl = "";
            routeUrl = (!string.IsNullOrWhiteSpace(AreaName) ? AreaName + "/" : "").ToLower();
            if(!string.IsNullOrWhiteSpace(ControllerName))
            {
                routeUrl += ControllerName.ToLower() + (!string.IsNullOrWhiteSpace(ActionName) ?  "/" : "");
            }

            routeUrl += ActionName.ToLower();

            var isUmPermit = userEntity.UnPermission.Where(x => x.RouteUrl.ToLower().Equals(routeUrl)).FirstOrDefault() != null;
            if (!isUmPermit)
            {
                isPermit = userEntity.Permission.Where(x => x.RouteUrl.ToLower().Equals(routeUrl)).FirstOrDefault() != null;
                if (isPermit)
                {
                    return Task.CompletedTask;
                }
            }

            WriteResult(filterContext, "對不起,您無權訪問");
            return Task.CompletedTask;
        }
    }

 

4.在action 上增加如下程式碼 即可

[Permission("Code", "名稱")]