mvc原始碼解讀(11)-mvc四大過濾器之AuthorizationFilter

風靈使發表於2018-07-10

在上一篇文章中,在講完ActionDescriptor這個類之後,我們直接跳過了這一句程式碼:

FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

直接講了ParameterDescriptor,現在我們回過頭來看GetFilters方法返回一個FilterInfo例項,FilterInfo類裡面的有如下成員:

public FilterInfo(IEnumerable<Filter> filters)
{
       var filterInstances = filters.Select(f => f.Instance).ToList();
       _actionFilters.AddRange(filterInstances.OfType<IActionFilter>());            
       _authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());            
       _exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());            
        _resultFilters.AddRange(filterInstances.OfType<IResultFilter>()); 
}

public IList<IActionFilter> ActionFilters {get {return _actionFilters;}}

public IList<IAuthorizationFilter> AuthorizationFilters {get {return _authorizationFilters;}}

public IList<IExceptionFilter> ExceptionFilters {get {return _exceptionFilters;}}

public IList<IResultFilter> ResultFilters {get {return _resultFilters;}}

我們可以看到FilterInfo裡面涉及到mvc的四大過濾器:AuthorizationFilterExceptionFilterActionFilterResultFilter。類似於.net平臺上的HttpModule對客戶端傳送過來的請求進行攔截從而達到某一種目的。我們知道在企業級的框架中,業務繁雜,對例如許可權處理,異常處理,日誌管理等這些非商業的業務,我們需要將它和業務處理抽離出來,一方面為了降低架構的耦合度,一方面可以方便我們的維護。而涉及到這些與業務無關的技術處理,比較流行的就是Aop面向切面程式設計的思想。Aop的核心思想在於:我引用部落格園上張逸的詳解http://wayfarer.cnblogs.com/articles/241024.html:”它利用一種稱為“橫切”的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任封裝起來,便於減少系統的重複程式碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關係,如果說“物件”是一個空心的圓柱體,其中封裝的是物件的屬性和行為;那麼面向方面程式設計的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的訊息。而剖開的切面,也就是所謂的“方面”了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。

使用“橫切”技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處都基本相似。比如許可權認證、日誌、事務處理。Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的高階方案構架師Adam Magee所說,AOP的核心思想就是“將應用程式中的商業邏輯同對其提供支援的通用服務進行分離”。而mvc的過濾器正是實現了Aop的這種“橫切“思想。

我們首先來看AuthorizationFilter授權過濾器,顧名思義就是對系統的許可權設計把關。而在mvc中所有的過濾器預設都繼承了抽象類FilterAttribute,我們來看看FilterAttribute裡面的一個重要成員:

 public int Order {
 get {return _order;}
 set {if (value < Filter.DefaultOrder) 
 {
 throw new ArgumentOutOfRangeException("value", MvcResources.FilterAttribute_OrderOutOfRange);}                
   _order = value;
 }        
}

屬性Order用於過濾器的排序順序,關於詳細Order的用處,大家可以參考部落格園上的蔣金楠老師的這一篇文章(深入探討ASP.NET MVC的篩選器):http://www.cnblogs.com/artech/archive/2012/07/02/filter.html。同時AuthorizationFilter還實現了介面IAuthorizationFilter裡面的OnAuthorization方法:

 public interface IAuthorizationFilter {
        void OnAuthorization(AuthorizationContext filterContext);
    }

實現類AuthorizeAttribute既繼承了抽象類FilterAttribute又實現了IAuthorizationFilter介面,因此如果我們要自定義一個授權過濾器,我們只要讓他繼承自AuthorizeAttribute類即可,我們先來看看AuthorizeAttribute類裡面的主要成員:

public string Roles 
{

 get {return _roles ?? String.Empty;}
 set {_roles = value;_rolesSplit = SplitString(value);}

}

public string Users 
{   

 get {return _users ?? String.Empty;}            
 set {_users = value;_usersSplit = SplitString(value);}        

}

protected virtual bool AuthorizeCore(HttpContextBase httpContext)

public virtual void OnAuthorization(AuthorizationContext filterContext)

protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
            // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
            filterContext.Result = new HttpUnauthorizedResult();
}



 internal static string[] SplitString(string original) { if (String.IsNullOrEmpty(original)) {return new string[0];}

            var split = from piece in original.Split(',')         

                            let trimmed = piece.Trim() 

                            where !String.IsNullOrEmpty(trimmed)                        

                            select trimmed;            

            return split.ToArray();        

}

裡面包含使用者Users和角色Roles屬性,AuthorizeCore方法用來實現授權檢查,HandleUnauthorizedRequest方法是當授權失敗時處理的動作,HandleUnauthorizedRequest定義如下:

public class HttpUnauthorizedResult : HttpStatusCodeResult {

        // HTTP 401 is the status code for unauthorized access. Other code might        

       // intercept this and perform some special logic. For example, the        

       // FormsAuthenticationModule looks for 401 responses and instead redirects        

       // the user to the login page.         private const int UnauthorizedCode = 401;

        public HttpUnauthorizedResult(): this(null) {}

        public HttpUnauthorizedResult(string statusDescription) : base(UnauthorizedCode, statusDescription) {}}

授權失敗之後預設是重定向到登入介面,如上英文所示。

我們前面也講過了要實現自定義的授權方式,只要繼承AuthorizeAttribute類並重寫裡面AuthorizeCore方法即可。

protected virtual bool AuthorizeCore(HttpContextBase httpContext) 
{            

            IPrincipal user = httpContext.User;            
            if (!user.Identity.IsAuthenticated) {

                   return false;

             }
            if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) {

                   return false;

              }

            if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) {

                   return false;

             }
                return true; 
}

因此我們在Controller中的授權如下:

[Authorization(Roles="admin",Users = "linghuchong")]
public ActionResult IsLogin()
{
    return View();
}

Action中貼上Authorization標籤之後,只有角色是“admin”且使用者名稱是“linghuchong”才有權訪問,這意味著,除非兩個條件都滿足,否則將不予授權,並會執行HandleUnauthorizedRequest方法。

相關文章