1.介紹 2.自定義 Filter V1.0 3.調試分析出現問題的原因 4.自定義 Filter V2.0 ...
深入瞭解 Authorize 和 AllowAnonymous
Chapter 0 - Intro
最近做的一個項目的時候,自定義授權 Attribute 來區分用戶許可權,我的項目不太大,許可權控制也不是很複雜,只涉及到匿名、普通用戶、超級管理員。 許可權驗證方式使用的是預設的 MemberShip 認證結合自己自定義的 許可權驗證 Filter。
Chapter 1 - 自定義 Filter V1.0
Filter代碼 V1.0
1 /// <summary> 2 /// 不需要登錄即可訪問 3 /// </summary> 4 public class NoPermissionRequiredAttribute : ActionFilterAttribute 5 { 6 public override void OnActionExecuting(ActionExecutingContext filterContext) 7 { 8 base.OnActionExecuting(filterContext); 9 } 10 } 11 12 /// <summary> 13 /// 需要登錄才能進行操作 14 /// </summary> 15 public class PermissionRequiredAttribute : ActionFilterAttribute 16 { 17 public override void OnActionExecuting(ActionExecutingContext filterContext) 18 { 19 if (filterContext.HttpContext.Session["User"]==null) 20 { 21 filterContext.Result = new RedirectResult("~/Admin/Account/Login"); 22 } 23 base.OnActionExecuting(filterContext); 24 } 25 } 26 27 /// <summary> 28 /// 需要有超級管理員許可權 29 /// </summary> 30 public class AdminPermissionRequiredAttribute : ActionFilterAttribute 31 { 32 public override void OnActionExecuting(ActionExecutingContext filterContext) 33 { 34 if ((filterContext.HttpContext.Session["User"] == null) || !((filterContext.HttpContext.Session["User"] as Models.User).IsSuper)) 35 { 36 filterContext.Result = new RedirectResult("~/Admin/Account/Login"); 37 } 38 base.OnActionExecuting(filterContext); 39 } 40 }
Chapter 2 - 控制器引用 Filter
控制器代碼中引用Filter 代碼:
1 [Authorize] 2 [Filters.PermissionRequired] 3 public class AccountController : BaseAdminController 4 { 5 /// <summary> 6 /// 登錄頁面 7 /// </summary> 8 /// <returns></returns> 9 [AllowAnonymous] 10 [Filters.NoPermissionRequired] 11 [HttpGet] 12 public ActionResult Login(string ReturnUrl) 13 { 14 if (!Url.IsLocalUrl(ReturnUrl)) 15 { 16 ReturnUrl = "/Admin/Home/Index"; 17 } 18 if (Helpers.AuthFormService.TryAutoLogin()) 19 { 20 return Redirect(ReturnUrl); 21 } 22 return View(); 23 } 24 25 /// <summary> 26 /// 賬戶首頁 27 /// </summary> 28 /// <returns></returns> 29 public ActionResult Index() 30 { 31 Models.User u = Session["User"] as Models.User; 32 return View(u); 33 } 34 }
Chapter 3 - 運行代碼
開始調試代碼,訪問這個 Login
Action 時就直接崩了,一直在重定向到登錄頁面。 於是就想為什麼會出現這樣的情況呢,只使用 [Authorize]
和 [AllowAnonymous]
的時候是不會出現這種問題的, 但是為什麼自定義 Filter 的時候會出現這樣的問題呢,是哪裡出現的問題呢。
首先自帶的[Authorize]
和 [AllowAnonymous]
是基於 就近原則 的,離的越近的 Filter 的許可權越高會覆蓋掉父級定義的 Filter。
但是自定義的 Filter 卻並沒有依照 就近原則 這一原則來控制許可權,所以可能內部並不是靠判斷哪個 Filter 離得近就用哪個 Filter的,下一步反編譯 MVC 代碼,看 MVC 是怎麼樣處理 [Authorize]
和 [AllowAnonymous]
的
Chapter 4 - 反編譯分析出現問題的原因
利用 .Net 反編譯工具 Reflector 或 JustDecompile反編譯 System.Web.Mvc.dll ,在命名空間 System.Web.Mvc 下可以找到 Authorize
和 AllowAnonymous
的定義,如下圖所示:
AllowAnonymous定義:
Authorize定義
通過上面的 Authorize.OnAuthorization 方法的定義基本可以知道問題出現在哪裡了,Authorize 在進行許可權驗證的時候會判斷當前請求的 Controller 和 Action 上是否有 AllowAnonymous 定義,如果有定義則不進行驗證,跳過驗證,只有在 當前請求的 Controller 和 Action 上都沒有 AllowAnonymous 時才會進行許可權驗證。
Chapter 5 - 自定義 Filter V2.0
知道問題出現在哪裡了,就開始修改自定義的 Filter 代碼吧,修改之後的代碼如下所示:
1 /// <summary> 2 /// 需要登錄才能進行操作 3 /// </summary> 4 public class PermissionRequiredAttribute : ActionFilterAttribute 5 { 6 7 public override void OnActionExecuting(ActionExecutingContext filterContext) 8 { 9 if (!filterContext.ActionDescriptor.IsDefined(typeof(NoPermissionRequiredAttribute),true)) 10 { 11 if (filterContext.HttpContext.Session["User"] == null) 12 { 13 filterContext.Result = new RedirectResult("~/Admin/Account/Login"); 14 } 15 } 16 base.OnActionExecuting(filterContext); 17 } 18 }
修改之後再次進行調試,導航到登錄頁面就不會再出現重定向的問題,就實現了按 就近原則 來決定 Filter 的優先順序的自定義的 Filter 了。