在總體介紹了篩選器及其提供機制(《深入探討ASP.NET MVC的篩選器》)之後,我們按照執行的先後順序對四種不同的篩選器進行單獨介紹,首先來介紹最先執行的AuthorizationFilter。從命名來看,AuthorizationFilter用於完成授權相關的工作,所以它應該在Action方法被調用之前執行才能起到授權的作用。不僅限於授權,如果我們希望目標Action方法被調用之前中斷執行的流程“做點什麼”,都可以以AuthorizationFilter的形式來實現。
一、IAuthorizationFilter
所有的AuthorizationFilter實現了介面IAuthorizationFilter。如下面的代碼片斷所示,IAuthorizationFilter定義了一個OnAuthorization方法用於實現授權的操作。作為該方法的參數filterContext是一個表示授權內容相關的AuthorizationContext對象, 而AuthorizationContext直接繼承自ControllerContext。
1: public interface IAuthorizationFilter
2: {
3: void OnAuthorization(AuthorizationContext filterContext);
4: }
5:
6: public class AuthorizationContext : ControllerContext
7: {
8: public AuthorizationContext();
9: public AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
10:
11: public virtual ActionDescriptor ActionDescriptor { get; set; }
12: public ActionResult Result { get; set; }
13: }
AuthorizationContext的ActionDescriptor屬性工作表示描述當前執行Action的ActionDescriptor對象,而Result屬性返回一個用於在授權階段呈現的ActionResult。AuthorizationFilter的執行是ActionInvoker進行Action執行的第一項工作,因為後續的工作(Model綁定、Model驗證、Action方法執行等)只有在成功授權的基礎上才會有意義。
ActionInvoker在通過執行AuthorizationFilter之前,會先根據當前的Controller上下文和解析出來的用於描述當前Action的ActionDescriptor,並以此建立一個表示授權內容相關的AuthorizationContext對象。然後將此AuthorizationContext對象作為參數,按照Filter對象Order和Scope屬性決定的順序執行所有AuthorizationFilter的OnAuthorization。
在所有的AuthorizationFilter都執行完畢之後,如果指定的AuthorizationContext對象的Result屬性工作表示得ActionResult不為Null,整個Action的執行將會終止,而ActionInvoker將會直接執行該ActionResult。一般來說,某個AuthorizationFilter在對當前請求實施授權的時候,如果授權失敗它可以通過設定傳入的AuthorizationContext對象的Result屬性響應一個“401,Unauthrized”回複,或者呈現一個錯誤頁面。
二、AuthorizeAttribute
如果我們要求某個Action只能被認證的使用者訪問,可以在Controller類型或者Action方法上應用具有如下定義的AuthorizeAttribute特性。AuthorizeAttribute還可以具體限制目標Action可被訪問的使用者或者角色,它的Users和Roles屬性用於指定被授權的使用者名稱和角色列表,中間用採用逗號作為分隔字元。如果沒有顯式地對Users和Roles屬性進行設定,AuthorizeAttribute在進行授權操作的時候只要求訪問者是被認證的使用者。
1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
2: public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
3: {
4: //其他成員
5: public virtual void OnAuthorization(AuthorizationContext filterContext);
6: protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext);
7:
8: public string Roles { get; set; }
9: public override object TypeId { get; }
10: public string Users { get; set; }
11: }
如果授權失敗(當前訪問者是未被授權使用者,或者目前使用者的使用者名稱或者角色沒有在指定的授權使用者或者角色列表中),AuthorizeAttribute會建立一個HttpUnauthorizedResult對象,並賦值給AuthorizationContext的Result屬性,意味著會響應一個狀態為“401,Unauthorized”的回複。如果採用Forms認證,配置的登入頁面會自動被顯示。
很多會將AuthorizeAttribute對方法的授權與PrincipalPermissionAttribute等同起來,實際上不但它們實現授權的機制不一樣(後者是通過代碼訪問安全檢驗實現對方法調用的授權),它們的授權策略也一樣。以下面定義的兩個方法為例,應用了PrincipalPermissionAttribute的FooOrAdmin意味著可以被帳號為Foo或者具有Admin角色的使用者訪問,而應用了AuthorizeAttribute特性的方法FooAndAdmin方法則只能被使用者Foo訪問,而且該使用者必須具有Admin角色。也就是說PrincipalPermissionAttribute特性對User和Role的授權邏輯是“邏輯或”,而AuthorizeAttribute 採用的則是“邏輯與”。
1: [PrincipalPermission( SecurityAction.Demand,Name="Foo", Role="Admin")]
2: public void FooOrAdmin()
3: { }
4:
5: [Authorize(Users="Foo", Roles="Admin")]
6: public void FooAndAdmin()
7: { }
除此之外,我們可以將多個PrincipalPermissionAttribute和AuthorizeAttribute應用到同一個類型或者方法上。對於前者,如果當前用於通過了任意一個PrincipalPermissionAttribute特性的授權就有權調用目標方法;對於後者來說,意味著需要通過所有AuthorizeAttribute特性的授權在具有了調用目標方法的許可權。以如下兩個方法為例,使用者Foo或者Bar可以有許可權調用FooOrBar方法,但是沒有任何一個使用者有權調用CannotCall方法(因為一個使用者只一個使用者名稱)。
1: [PrincipalPermission( SecurityAction.Demand, Name="Foo")
2: [PrincipalPermission( SecurityAction.Demand, Name="Bar")]
3: public void FooOrBar()
4: { }
5:
6: [Authorize(Users="Foo")]
7: [Authorize(Users="Bar")]
8: public void CannotCall()
9: {}