WebForm
在做WebForm的時候,如果我們要實現某頁面登陸后才能訪問,這個非常容易實現
public partial class IndexForm : Page { protected void Page_Load(object sender, EventArgs e) { //檢查是否登錄(session/cookie),失敗跳轉登錄,成功繼續訪問 } }
但是實際工作中,不會只有一個頁面需要權限檢查,當我們面對多個頁面的時候,該如何處理呢?這個時候一般會采取下列這種處理方式:
1 public partial class IndexForm : BasePage 2 { 3 protected void Page_Load(object sender, EventArgs e) 4 { 5 //do something 6 } 7 } 8 9 public partial class BasePage : Page 10 { 11 /// <summary> 12 /// Pre_Init頁面加載最早發生的事件 13 /// </summary> 14 /// <param name="sender"></param> 15 /// <param name="e"></param> 16 public void Pre_Init(object sender, EventArgs e) 17 { 18 //檢查用戶登錄(session/cookie)成功就繼續,失敗就跳轉到登陸頁 19 //需要檢查的頁面就繼承BasePage 20 if (true) 21 { 22 //繼續訪問 23 } 24 else 25 { 26 //跳轉登錄 27 } 28 } 29 }
流程圖
這是一個通過繼承方式的解決方法,當我們頁面需要權限驗證的時候,只需要繼承我們的帶有驗證方法的BasePage,而不需要驗證的頁面,只需要繼承BasePageWithoutAuth,但是,當功能比較復雜的時候,用戶登錄還能用BasePage處理, 而異常處理,日志處理,緩存處理等這些可能這種方式就解決不了, 就必須在每個頁面內處理完成,這就導致了很多頁面重復出現了相同功能的代碼
filter與AOP
AOP(Aspect Oriented Programming)面向切面編程:在不破壞類型封裝的前提下,額外的添加功能.
filter即過濾器,是AOP思想在mvc中的一種具體實現,對於webform中出現的上述問題,在mvc框架中能夠用filter以AOP的方式解決,
過濾器是在執行某一個方法之前,先去執行其他的某些操作,當執行完成后再確定后續動作。相當於在我們具體方法的業務邏輯之外又額外的添加了一些功能,例如權限認證,異常處理,日志記錄等,我們可以把我們的業務邏輯與這些功能組合起來,而又不會被影響
filter之權限認證自定義擴展
上面說到過濾器filter可以實現請求方法前做權限校驗、登錄校驗等,比如說只有登錄的用戶才可以訪問這個方法,需要進行Session的校驗。如果有很多的控制器中的方法都需要校驗session,后期維護也是非常的不方便的,所以只需要將這種校驗放在Filter中就可以了。
MVC框架自帶有默認權限認證的特性[Authorize]

1 namespace System.Web.Mvc 2 { 3 // 4 // 摘要: 5 // 指定對控制器或操作方法的訪問只限於滿足授權要求的用戶。 6 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] 7 public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter 8 { 9 // 10 // 摘要: 11 // 初始化 System.Web.Mvc.AuthorizeAttribute 類的新實例。 12 public AuthorizeAttribute(); 13 14 // 15 // 摘要: 16 // 獲取或設置有權訪問控制器或操作方法的用戶角色。 17 // 18 // 返回結果: 19 // 有權訪問控制器或操作方法的用戶角色。 20 public string Roles { get; set; } 21 // 22 // 摘要: 23 // 獲取此特性的唯一標識符。 24 // 25 // 返回結果: 26 // 此特性的唯一標識符。 27 public override object TypeId { get; } 28 // 29 // 摘要: 30 // 獲取或設置有權訪問控制器或操作方法的用戶。 31 // 32 // 返回結果: 33 // 有權訪問控制器或操作方法的用戶。 34 public string Users { get; set; } 35 36 // 37 // 摘要: 38 // 在過程請求授權時調用。 39 // 40 // 參數: 41 // filterContext: 42 // 篩選器上下文,它封裝有關使用 System.Web.Mvc.AuthorizeAttribute 的信息。 43 // 44 // 異常: 45 // T:System.ArgumentNullException: 46 // filterContext 參數為 null。 47 public virtual void OnAuthorization(AuthorizationContext filterContext); 48 // 49 // 摘要: 50 // 重寫時,提供一個入口點用於進行自定義授權檢查。 51 // 52 // 參數: 53 // httpContext: 54 // HTTP 上下文,它封裝有關單個 HTTP 請求的所有 HTTP 特定的信息。 55 // 56 // 返回結果: 57 // 如果用戶已經過授權,則為 true;否則為 false。 58 // 59 // 異常: 60 // T:System.ArgumentNullException: 61 // httpContext 參數為 null。 62 protected virtual bool AuthorizeCore(HttpContextBase httpContext); 63 // 64 // 摘要: 65 // 處理未能授權的 HTTP 請求。 66 // 67 // 參數: 68 // filterContext: 69 // 封裝有關使用 System.Web.Mvc.AuthorizeAttribute 的信息。filterContext 對象包括控制器、HTTP 上下文、請求上下文、操作結果和路由數據。 70 protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext); 71 // 72 // 摘要: 73 // 在緩存模塊請求授權時調用。 74 // 75 // 參數: 76 // httpContext: 77 // HTTP 上下文,它封裝有關單個 HTTP 請求的所有 HTTP 特定的信息。 78 // 79 // 返回結果: 80 // 對驗證狀態的引用。 81 // 82 // 異常: 83 // T:System.ArgumentNullException: 84 // httpContext 參數為 null。 85 protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext); 86 } 87 }
但是框架的自帶東西有時候很難滿足我們的需要,在此基礎上我們可以繼承AuthorizeAttribute,然后復寫擴展(一般而言,帶有virtual關鍵字的,都可以復寫)
例如檢查請求地址是否帶有Account和Password的參數,值分別為Admin和123456,如果滿足,則可以繼續訪問,否則返回指定的路徑
1 public class CustomerAuthorizeAttribute: AuthorizeAttribute 2 { 3 private string LoginUrl = string.Empty; 4 public CustomerAuthorizeAttribute() 5 { 6 LoginUrl = "/Home/Login"; 7 } 8 9 public CustomerAuthorizeAttribute(string loginUrl) 10 { 11 LoginUrl = loginUrl; 12 } 13 /// <summary> 14 /// 請求進入具體action前,先執行OnAuthorization方法 15 /// </summary> 16 /// <param name="filterContext"></param> 17 public override void OnAuthorization(AuthorizationContext filterContext) 18 { 19 //base.OnAuthorization(filterContext); 20 //filterContext.HttpContext 能拿到HttpContext,意味着我們能拿到一切請求的信息 21 if (filterContext.HttpContext.Request.QueryString["Account"]=="Admin" 22 && filterContext.HttpContext.Request.QueryString["Password"] == "123456") 23 { 24 return; 25 } 26 else 27 { 28 filterContext.Result = new RedirectResult(LoginUrl); 29 } 30 } 31 32 }
只需要在需要權限認證的方法上加上[CustomerAuthorizeFilter]特性即可.
這就是AOP的魅力所在,只用加上一個特性,就完成了一個驗證權限的功能,而且本身的業務邏輯封裝沒有一絲的破壞
Filter的三種注冊方式
1方法注冊
只給一個方法加權限認證的特性,那么就只會有特定的方法才會有權限驗證
2類注冊
給某個控制器加權限認證的特性,那么該控制器下所有的方法都有權限驗證,但是要注意死循環,如果驗證失敗返回的路徑也在該控制器下,那么會再次驗證,然后返回再驗證,一直重定向,最后重定向次數過多,頁面崩潰
[CustomerAuthorize]//類注冊 public class HomeController : Controller { [CustomerAuthorize("Home/About")]//方法注冊 public ActionResult Index() { return View(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } //此處若是類注冊,且為失敗返回的路徑,則會死循環 public ActionResult Login() { ViewBag.Message = "Your Login page."; return View(); } }
3全局注冊
在Global.asax全局配置文件里面,有過濾器的配置文件FilterConfig.cs,在App_Start目錄下,如果在這里面注冊上述的權限認證特性,則所有的頁面都會有權限驗證
1 public class FilterConfig 2 { 3 public static void RegisterGlobalFilters(GlobalFilterCollection filters) 4 { 5 filters.Add(new CustomerAuthorizeAttribute());//全局注冊 6 filters.Add(new HandleErrorAttribute());//全局異常處理 7 } 8 }
匿名登錄
如果我們類注冊了權限驗證特性,但是在該類下面有幾個需要匿名登陸的方法,那么是否意味着需要將這幾個方法移到其他的控制器呢?其實不然,我們只需要給這些方法加上AllowAnonymous特性,在上述自定義特性里面先判斷時候是否加了該特性即可.這里其實用什么做匿名特性都可以,只是AllowAnonymous是框架自帶的一個匿名特性,就直接用了,如果想用自定義的匿名特性,再創建一個特性就可以了,什么都不需要加,只是用於標記.
1 if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute),true)) 2 { 3 return; 4 }
出自:博客園-半路獨行
原文地址:https://www.cnblogs.com/banluduxing/p/9185182.html
本文出自於http://www.cnblogs.com/banluduxing 轉載請注明出處。
代碼地址:https://github.com/weiweu/My-space/tree/master/Design/DecoratorPattern