最近比較忙,部落格很久沒更新了,很多博友問何時更新博文,是以,今天就花了點時間,寫了本篇文章,但願大家喜歡。
本篇文章不适合初學者,需要對ASP.NET MVC具有一定基礎。
本篇文章主要從ASP.NET MVC 基架角度去分析MVC架構是如何實作資源過濾,資源授權,感興趣的,歡迎閱讀。
相關文章,請參與ASP.NET MVC系列
一 ASP.NET MVC架構驗證機制
為了更加透徹地了解MVC的過濾機制,我簡要地畫了如下UML圖。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL5YDOzgjN2kzMx0iM4ETOyUDN0EjMycDM4EDMy0yMykjN2ATMvw1NwgTMwIzLcNjM5YjNwEzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
下面,我們根據如上的UML圖來簡要分析一下。
(一)MVC基架過濾機制根接口
根接口:本文指在繼承關系中的根節點,相當于類的祖宗對象Object。
從上圖中,很容易看出MVC過濾機制的根接口主要有六大類型接口:_Attribute,IMvcFilter,IActionFilter,IResultFilter,IExceptionFilter和IAuthorizationFilter。
1._Attribute
_Attribute接口主要提供一些非托管服務。
2.IMvcFilter
該接口主要用于定義篩選器順序和是否允許多個篩選器,如當我們給某個action限定多個特性時的執行順序問題。
[Filter1(Order = 3)]
[Filter2(Order = 2)]
[Filter3(Order = 1)]
public ActionResult Index()
{
return View(); ;
}
3.IActionFilter
該接口主要定義操作的篩選,在執行操作方法前後,分别調用方法OnActionExecuting(ActionExecutingContext filterContext)和方法OnActionExecuted(ActionExecutedContext filterContext)。IResult也有類似兩個方法。
4.IResultFilter
該接口主要用作操作結果的篩選。對于MVC模式開發的程式員來說,ActionResult再熟悉不過了,然而,MVC機制會在操作結果執行前後,分别執行OnResultExecuting(ResultExecutingContext filterContext)方法和OnResultExecuted(ResultExecutedContext filterContext)方法,然後再将最終結果Responce給使用者,
5.IExceptionFilter
該接口主要是用來處理系統異常問題,如在項目中經常用到的throw new Exception();
6.IAuthorizationFilter
該接口用來定義資源的篩選和授權,在項目中,主要用做權限管理。
(二) 三個抽象接口
從上圖繼承圖中,不難看出,MVC過濾機制,提供了三個重要的抽象類,分别為:_Attribute,FilterAttribute和ActionFilterAttribute
1.ActionFilterAttribute
從繼承圖中知道,ActionFilterAttribute繼承FilterAttribute抽象類,實作IAcionFilter接口和IResultFilter接口。
反編譯代碼如下:
不難看出,ActionFilterAttribute共有四個虛方法和一個可被繼承構造函數,而四個虛方法,分别是IAcionFilter接口和IResultFilter方法簽名的原型。
四個虛方法,在上面我們已經分析過,這裡就不分析了,重點分析一下這四個方法的執行順序:
=》Request ip=>Route(路由器尋找具體Controller和action)=>執行action上面的特性=》OnActionExecutiong=》執行方法=》OnActionExecuted=》OnResultExecuting=》執行操作結果=》OnResultExecuted=》将執行結果最終Responce給浏覽器。
源碼:
public class ExeOrderTestAttribute:ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
base.OnResultExecuting(filterContext);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = String.Format("{0} controller: {1} action: {2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "ASP.NET MVC Filter ExecuteOrder");
}
}
(三)其他類
從上面的繼承圖中可以看出,ASP.NET MVC過濾機制,處理定義接口,抽象類外,還有一些其他基礎類,比較關鍵的類有:OutputCacheAttribute,ValidateInpuntAttribute,AuthorizeAttribute和ValidateAntiForgeryTokenAttribute。
1.OutputCacheAttribute
該類主要是解決Cache問題,感興趣的,可以看看淺談緩存技術在ASP.NET中的運用 ,這裡不累述。
2.ValidateInpuntAttribute 和ValidateAntiForgeryTokenAttribute
主要解決表單驗證問題,如防止漏洞注入,JS注入等問題。
3.AuthorizeAttribute
該類主要解決資源授權問題,也就是平時大家所說的權限管等問題,是非常重要的一個類,是以将在本篇文章的第二部分單獨講解。
二 MVC架構授權機制
ASP.NET MVC架構通過AuthorizeAttribute類實作資源的權限控制通路。
我們先來通過反彙編看看AuthorizeAttribute。
1 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
2 public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
3 {
4 // Fields
5 private string _roles;
6 private string[] _rolesSplit = new string[0];
7 private static readonly char[] _splitParameter = new char[] { ',' };
8 private readonly object _typeId = new object();
9 private string _users;
10 private string[] _usersSplit = new string[0];
11
12 // Methods
13 protected virtual bool AuthorizeCore(HttpContextBase httpContext)
14 {
15 if (httpContext == null)
16 {
17 throw new ArgumentNullException("httpContext");
18 }
19 IPrincipal user = httpContext.User;
20 if (!user.Identity.IsAuthenticated)
21 {
22 return false;
23 }
24 if ((this._usersSplit.Length != 0) && !this._usersSplit.Contains<string>(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
25 {
26 return false;
27 }
28 if ((this._rolesSplit.Length != 0) && !this._rolesSplit.Any<string>(new Func<string, bool>(user.IsInRole)))
29 {
30 return false;
31 }
32 return true;
33 }
34
35 private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
36 {
37 validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
38 }
39
40 protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
41 {
42 filterContext.Result = new HttpUnauthorizedResult();
43 }
44
45 public virtual void OnAuthorization(AuthorizationContext filterContext)
46 {
47 if (filterContext == null)
48 {
49 throw new ArgumentNullException("filterContext");
50 }
51 if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
52 {
53 throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
54 }
55 if (!filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) && !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
56 {
57 if (this.AuthorizeCore(filterContext.HttpContext))
58 {
59 HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
60 cache.SetProxyMaxAge(new TimeSpan(0L));
61 cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), null);
62 }
63 else
64 {
65 this.HandleUnauthorizedRequest(filterContext);
66 }
67 }
68 }
69
70 protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
71 {
72 if (httpContext == null)
73 {
74 throw new ArgumentNullException("httpContext");
75 }
76 if (!this.AuthorizeCore(httpContext))
77 {
78 return HttpValidationStatus.IgnoreThisRequest;
79 }
80 return HttpValidationStatus.Valid;
81 }
82
83 internal static string[] SplitString(string original)
84 {
85 if (string.IsNullOrEmpty(original))
86 {
87 return new string[0];
88 }
89 return original.Split(_splitParameter).Select((<>c.<>9__19_0 ?? (<>c.<>9__19_0 = new Func<string, <>f__AnonymousType1<string, string>>(<>c.<>9.<SplitString>b__19_0)))).Where((<>c.<>9__19_1 ?? (<>c.<>9__19_1 = new Func<<>f__AnonymousType1<string, string>, bool>(<>c.<>9.<SplitString>b__19_1)))).Select((<>c.<>9__19_2 ?? (<>c.<>9__19_2 = new Func<<>f__AnonymousType1<string, string>, string>(<>c.<>9.<SplitString>b__19_2)))).ToArray<string>();
90 }
91
92 // Properties
93 public string Roles
94 {
95 get
96 {
97 return (this._roles ?? string.Empty);
98 }
99 set
100 {
101 this._roles = value;
102 this._rolesSplit = SplitString(value);
103 }
104 }
105
106 public override object TypeId
107 {
108 get
109 {
110 return this._typeId;
111 }
112 }
113
114 public string Users
115 {
116 get
117 {
118 return (this._users ?? string.Empty);
119 }
120 set
121 {
122 this._users = value;
123 this._usersSplit = SplitString(value);
124 }
125 }
126
127 // Nested Types
128 [Serializable, CompilerGenerated]
129 private sealed class <>c
130 {
131 // Fields
132 public static readonly AuthorizeAttribute.<>c <>9 = new AuthorizeAttribute.<>c();
133 public static Func<string, <>f__AnonymousType1<string, string>> <>9__19_0;
134 public static Func<<>f__AnonymousType1<string, string>, bool> <>9__19_1;
135 public static Func<<>f__AnonymousType1<string, string>, string> <>9__19_2;
136
137 // Methods
138 internal <>f__AnonymousType1<string, string> <SplitString>b__19_0(string piece)
139 {
140 return new { piece = piece, trimmed = piece.Trim() };
141 }
142
143 internal bool <SplitString>b__19_1(<>f__AnonymousType1<string, string> <>h__TransparentIdentifier0)
144 {
145 return !string.IsNullOrEmpty(<>h__TransparentIdentifier0.trimmed);
146 }
147
148 internal string <SplitString>b__19_2(<>f__AnonymousType1<string, string> <>h__TransparentIdentifier0)
149 {
150 return <>h__TransparentIdentifier0.trimmed;
151 }
152 }
153 }
154
155
156 Collapse Methods
157
View Code
通過反彙編,得出如下結論:
1.提供四個虛方法OnAuthorization(AuthorizationContext filterContext),AuthorizeCore(HttpContextBase httpContext),HandleUnauthorizedRequest(AuthorizationContext filterContext)和OnCacheAuthorization(HttpContextBase httpContext)。
四個方法作用和關系是怎樣的呢
OnAuthorization表示請求過程時身份驗證,AuthorizeCore表示支援使用者自定義身份驗證,HandleUnauthorizeRequest表示當AuthorizeCore方法驗證失敗時,執行的操作,OnCacheAuthorization表示子產品緩存權限。
(一) ASP.NET MVC 基架提供的驗證
隻需在需要驗證的資源生加上[Authorize]特性即可。
1 [Authorize]
2 public ActionResult Index()
3 {
4 return View(); ;
5 }
(二) 實作自定義驗證機制
當未給Index() action添加驗證限制時,匿名使用者名能直接通路 /Default/Index
當給Index() action添加驗證限制時,匿名使用者名不能直接通路 /Default/Index,而被強制重定型到登陸頁面,要求使用者登陸。
(三) ASP.NET MVC驗證級别
ASP.NET MVC提供了四種基本驗證模式:匿名,全局,控制器和Action
1.匿名
1 public class DefaultController : Controller
2 {
3 [AllowAnonymous]
4 public ActionResult Index()
5 {
6 return View();
7 }
8 }
2.action
1 public class DefaultController : Controller
2 {
3 [CustomAuthorize]
4 public ActionResult Index()
5 {
6 return View();
7 }
8 }
3.Controller
1 [CustomAuthorize]
2 public class DefaultController : Controller
3 {
4 public ActionResult Index()
5 {
6 return View();
7 }
8 }
4.全局
1 public class FilterConfig
2 {
3 public static void RegisterGlobalFilters(GlobalFilterCollection filters)
4 {
5 filters.Add(new CustomAuthorizeAttribute());//給自定義驗證注冊
6 filters.Add(new HandleErrorAttribute());
7 }
8 }
擴充,當添加多個驗證時,程式時如何執行的?通古Order值順序執行
1 [Filter1(Order = 3)]
2 [Filter2(Order = 2)]
3 [Filter3(Order = 1)]
4 public ActionResult Index()
5 {
6 return View(); ;
7 }
三 總結
本篇文章主要從ASP.NET MVC架構設計角度,運用PD工具,通過UML分析ASP.NET MVC是如何實作資源過濾,如何實作權限控制等功能的,更細粒度的技術文章,在後續博文中更新。