MVC5+EF6 --自定義控制Action訪問權限


本章主要講解在MVC中靈活控制Action的訪問權限;

本章所使用的示例表也是上一張所使用的TbUser、TbRole、TbUserRole;

最終的效果是針對任意一個Action或Controller,都可以根據配置的角色來控制訪問權限;

完成此核心功能后,可以再往兩方面擴展常用功能:

1. 可以根據 組織/用戶/角色 的並集來控制權限

2. 以此核心功能為基礎,實現菜單的動態配置

PS:擴展功能本章暫時不

文章提綱

  • 概述
  • 理論基礎
  • 詳細步驟
  • 總結

概述

一、MVC Form認證身份基礎

通常用法舉例:

1. web.config配置,開啟form認證

2. 需要認證的 Control或Action 上添加過濾,例如限制只有用戶"zhangsan"或者角色"管理員"可以訪問

    

另外還有一種常用形式表示:只要有用戶登錄就可以訪問

就是直接在Action或整個Controller上[Authorize]屬性過濾

二、為什么需要自定義MVC權限過濾器

上述解決方式中很明顯會發現有兩個缺點:

1. 修改權限時需在Action, Controller上修改后需重新編譯,不靈活。

2.過濾器中的Role是內置對象,如果不使用ASP.NET自身的集成權限方案,就無法按照角色來過濾。

解決這兩個問題,只需要擴展類AuthorizeAttribute即可。

理論基礎

為了能使用自定義的角色控制權限,我們需要擴展或繞過 ASP.NET 的Membership和Role provider 框架。

1.擴展:實現自定義的 Membership/Role provider

2.繞過:直接不使用

 

我們選擇繞過的方式,這樣的話更加靈活。

(因為如果你的角色結構和系統不一致,用擴展的方式弄起來比較麻煩)

我們使用form認證的三個核心API, 只用這幾個API既可以減少工作量,又可以和Membership/Role provider保持獨立。

1. FormsAuthentication.SetAuthCookie

用戶登錄后,指定用戶名

2. Request.IsAuthenticated

登錄后返回true

3. HttpContext.Current.User.Identity.Name

返回登錄的用戶名

 

權限過濾的完整過程:

1. Authetication ( 登錄 )

登錄成功后,調用 FormsAuthentication.SetAuthCookie 設置一個用戶名。

2. Authorization(授權)

新建自定義的授權屬性類:CustomAuthorizeAttribute(繼承於AuthorizeAtrribute),擴展權限過濾器

3. 類似於默認Authorize attribute的使用方法,附加自定義的authorize attribute到controller或action上去,實現權限過濾

詳細步驟

一、啟用form認證,完成登錄/退出 基本功能

1. 啟用 form 認證

配置web.config,啟用form認證

2. 完成登錄/退出 基本功能

新建Controller: AccountController.cs

    public class AccountController : Controller
    {
        private MyDbContext db = new MyDbContext();
        // GET: Account
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Login()
        {
            ViewBag.LoginState = "登錄前...";
            TempData["returnUrl"] = Request["ReturnUrl"];
            //如果當前有登錄用戶,就需要跳轉到權限不足提示頁面
            if (!string.IsNullOrEmpty(User.Identity.Name))
            {
                return View("NoPermissions");
            }
            else
            {
                return View();
            }
        }
        [HttpPost]
        public ActionResult Login(TbUser user)
        {
            string url = Convert.ToString(TempData["returnUrl"]);
            var userinfo = db.TbUsers.FirstOrDefault(u => u.Email == user.Email && u.Password == user.Password);
            if (userinfo != null)
            {
                FormsAuthentication.SetAuthCookie(userinfo.UserName, false);
                if (!string.IsNullOrEmpty(url))
                {
                    ViewBag.LoginState = userinfo.UserName + "登錄后...";
                    return Redirect(url);
                }
                else
                {
                    return Redirect("~/");
                }

            }
            else
            {
                ViewBag.LoginState = user.Email + "用戶不存在...";
            }
            return View();
        }

        public ActionResult Logout()
        {
            FormsAuthentication.SignOut();
            return Redirect(Request.UrlReferrer.ToString());
        }
        /// <summary>
        /// 權限不足頁面
        /// </summary>
        /// <returns></returns>
        public ActionResult NoPermissions()
        {
            return View();
        }

    }
View Code

登錄功能:

登出功能:

這里對應的視圖文件就不展示了

二、准備好權限配置文件

1. 用到的基礎數據:用戶,角色及用戶/角色 關系

2. 角色與Action對應的權限關系

這里我們先用一個xml代替,理解這個后就可以擴展到DB中。

新建文件夾Config,新建ActionRoles文件,配置Action/Role的對應關系

PS:

Action未配置情況下,默認有訪問權限;

Action 配置角色為空,有訪問權限。

 

三、擴展 AuthorizeAttribute

1. 新建類CustomAuthorizeAttribute,繼承與AuthorizeAttribute

override兩個方法:

在請求授權時調用:

提供一個入口點用於自定義授權檢查,通過為true

CustomAuthorizeAttribute.cs(兩個方法的具體實現):

    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        private MyDbContext db = new MyDbContext();

        /// <summary>
        /// 對應Action允許的角色
        /// </summary>
        private string[] AuthRoles { get; set; }
        /// <summary>
        /// 在請求授權時調用
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("HttpContext");
            }
            if (AuthRoles == null || AuthRoles.Length == 0)
            {
                return false;
            }
            #region 確定當前用戶角色是否屬於指定的角色
            //獲取當前用戶所在角色
            string sql = "select RoleName from TbRole where Id in(select roleId "
                        + "from TbUserRole where userid = "
                        + "(select id from TbUser where UserName=@UserName))";
            string currentUser = httpContext.User.Identity.Name;
            SqlParameter[] paras = new SqlParameter[] { 
                new SqlParameter("@UserName",currentUser)
            };
            var userRoles = db.Database.SqlQuery<string>(sql, paras).ToList();
            //驗證是否屬於對應角色
            for (int i = 0; i < AuthRoles.Length; i++)
            {
                if (userRoles.Contains(AuthRoles[i]))
                {
                    return true;
                }
            }
            #endregion
            return false;
        }
        /// <summary>
        /// 提供一個入口點用於自定義授權檢查,通過為true
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            string actionName = filterContext.ActionDescriptor.ActionName;
            //獲取config文件配置的action允許的角色,以后可以轉到數據庫中
            string roles = GetActionRoles(actionName, controllerName);
            if (!string.IsNullOrWhiteSpace(roles))
            {
                this.AuthRoles = roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            }
            else {
                this.AuthRoles = new string[] { };
            }
            base.OnAuthorization(filterContext);
        }
        /// <summary>
        /// 根據當前Controller和Action名稱獲取對應節點內容
        /// </summary>
        /// <param name="action">Action名稱</param>
        /// <param name="controller">Controller名稱</param>
        /// <returns></returns>
        private static string GetActionRoles(string action, string controller)
        {
            XElement rootElement = XElement.Load(HttpContext.Current.Server.MapPath("~/Configs/") + "ActionRoles.xml");
            XElement controllerElement = FindElementByAttribute(rootElement, "Controller", controller);
            if (controllerElement != null)
            {
                XElement actionElement = FindElementByAttribute(controllerElement, "Action", action);
                if (actionElement != null)
                {
                    return actionElement.Value;
                }
            }
            return "";
        }

        private static XElement FindElementByAttribute(XElement xelement, string tagName, string attribute)
        {
            XElement nowXelement = xelement.Elements(tagName).FirstOrDefault(e => e.Attribute("name").Value.Equals(attribute, StringComparison.OrdinalIgnoreCase));
            return nowXelement;
        }
    }
View Code

 

總結

在此,權限控制的整個過程就差不多了,我們來測試一下。

新建HomeController, 新建一些Action做測試(Index, About,Install,Other)

回顧一下基礎數據。

HomeController.cs

namespace TestMVC.Controllers
{
    [CustomAuthorizeAttribute]
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
        public ActionResult Install()
        {
            return View();
        }
        public ActionResult Other()
        {
            return View();
        }
    }
}
View Code

對應視圖:

 

補充說明:

如下圖,可以設置為全局。

這樣就不需要單個設置,對所有Action應用自定義過濾條件。

 源碼下載


免責聲明!

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



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