基於資源名的MVC權限控制


  在程序復雜程度不斷上升的過程中,無可避免需要觸碰到權限控制,而權限控制又與業務邏輯緊緊相關,市場上出現了大量的權限控制產品,而程序的開發,講究去繁化簡的抽象,在我的開發過程中,逐漸發現程序的權限控制核心不外乎兩個方面:1、資源定位;2、訪問控制列表。本文主要針對資源定位進行分析,並解決一些我所遇見過的問題。而在MVC上,MVC提供給我們了非常好的訪問控制擴展機制,我們能夠通過這些機制更好地控制系統權限。

  在我們之前的開發中,針對ASP.NET下WebForm進行開發,很多人都采用了繼承Page基類自定義BasePage,構造自己的驗證邏輯后再將最終的展示頁面繼承於BasePage,所有的驗證邏輯中,也必須解決我上文中提到的兩個問題:資源定位和訪問控制列表。而WebForm下使用路由機制的機會並不多,意義也並不大,在這個背景下,大多數人產生了這樣的錯覺:使用URL進行資源定位就夠了。我所見過的幾個項目,大多采用了這個辦法,或者這個辦法的細微變種。而WebForm的機制也決定了這個方法是具有一定可行性的,每一個資源(頁面)都是一個類,資源定位較為容易。而進入MVC時代后,一切都發生了改變。在MVC的場景下,你將遇到以下幾個問題:

  1、  路由機制的引入會出現一個資源多個URL的可能以及增加Area等參數后URL的不確定性。

  2、  Action重載的問題難以解決。例如一個頁面Order,在Get方法下不帶參訪問是被允許的,而在Post下帶參訪問是被禁止的,但這兩個資源的URL都是 /Order/,在一些第三方庫的輔助下,甚至能有更多的重載Action,這就導致URL定位機制的全面失效。

 

  而面對上述情況,用一種什么樣的方式進行資源的定位、控制就成為放在我們面前的難題。在MVC下,可以認為每一個方法(Action)即為一個資源(頁面),用何種方法能在程序外部控制這些資源使得權限控制能對其輕松進行,這是整個問題的核心。上文說到,每個方法即為一個資源,那我們是否可以將方法名、方法簽名作為資源的定位標識?答案是可以的。

  在MVC下,進行權限控制,很自然地想到了使用自定義AuthorizationFilter來進行控制,那么在這個Attribute中是可以獲得方法相關信息的。

var t = (ReflectedActionDescriptor) filterContext.ActionDescriptor;
var method= t.MethodInfo.ToString();

  在派生自FilterAttribute, IAuthorizationFilter的自定義Attribute中,可以根據上面兩個方法獲取方法的完整簽名,包括返回類型、方法名、參數類型。

  通過這個方法是可以進行權限控制的,但是這個方法存在着一個致命的缺點:返回值的類型名、參數的類型名,有的是完全限定名,即需要帶命名空間,而有的是不完全限定名。而這個完全限定名的獲取是較為繁瑣的,因此,這個方法的可操作性大大降低。

  那如何優雅地定位到我們所需要控制的資源呢?我們是否可以為我們所需要的資源取一個名字,然后在訪問控制列表中將這個名字添加進去,每次執行這個Action的時候獲取當前Action的名字,然后在訪問控制列表中進行比對就可以解決這個問題。那如何將資源進行命名就成了解決問題的關鍵。代碼級的資源控制必須想到元數據,而需要在元數據中增加信息,這個任務自然又落到Attribute的身上。

  我們自定義一個Attribute為Action標注資源名稱:

  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public sealed class ResourceCustomerNameAttribute : Attribute
    {
        private readonly string _resourceName;

        public ResourceCustomerNameAttribute(string resourceName)
        {
            _resourceName = resourceName;
        }

        public string ResourceName
        {
            get { return _resourceName; }
        }
    }

  這個Attribute實現的功能很簡單:為資源進行命名(如果方便,使用ID會更高效一點)。

  完成了對資源的命名,接下來需要對資源與訪問控制列表進行比對,我是這樣使用的:繼承FilterAttribute, IAuthorizationFilter,在派生類中先獲取ResourceCustomerNameAttribute實例

  然后用attribute.ResourceName屬性與我們的訪問控制列表進行比對:

    public void OnAuthorization(AuthorizationContext filterContext)
        {
            var acl = filterContext.HttpContext.Session[SessionName.PermissionSessionName] as IEnumerable<IAcl>;
                
            var attNames = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ResourceCustomerNameAttribute), true) as IEnumerable<ResourceCustomerNameAttribute>;

            var anonymous =filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;

            if (anonymous != null && anonymous.Any())
            {
                return;
            }
            if (acl == null || !acl.Any())
            {
                filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = _controller, action = _action }));
            }
            else
            {
                var joinResult = (from aclEntity in acl
                                  join attName in attNames on aclEntity.ResourceName equals attName.ResourceName
                                  select attName
                ).Any();

                if (joinResult)
                {
                    return;
                }
                else
                {
                    filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = _controller, action = _action }));
                }
            }
        }

  如果比對成功則獲取相應訪問許可,可以對資源進行訪問,若比對失敗則跳轉至授權失敗頁面。

  而在實際的使用過程中,我們在登錄成功后,將構造用戶的ACL,並將ACL存入Session中,之后用戶在每一次的訪問中均使用Session中的ACL進行比對,授權。

  在這個具體實現中,我有一個難以處理的問題:即用戶訪問頁面時若未授權,則跳轉到未授權提示頁面,若用戶進行基於Json傳遞的ajax調用時如何向用戶提供合適的信息?之前考慮過使用ContentType來判斷,可是后來覺得這會導致開發之中的約定過多(更何況我對很多人對Http協議的了解程度並不抱有信心)。不知各位是否有更好的辦法來實現多種錯誤返回信息?望不吝賜教。

  模塊的具體代碼已托管於GitHub:https://github.com/uliian/ULiiAnPermissionControlModule

  功能核心模塊已經上傳,示例正被GFW狂虐~各位稍安勿躁,如有靠譜的全局科學上網方法的同學也希望悄悄告訴我一下~T.T

  歡迎參考,提出您的寶貴意見。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM