閱讀711 返回首頁    go 阿裏雲 go 技術社區[雲棲]


利用EntLib授權機製實現對ASP.NET頁麵的自動授權

ASP.NET默認采用UrlAuthorizationModuleFileAuthorizationModule分別實現針對請求地址和物理文件的授權,但是在很多情況下我們需要額外的授權方式。Entlib提供了一種基於表達式的授權方式,它允許我們以一個表達式的方式來定義授權的規則。在新的項目中我們希望利用EntLib的授權框架來實現針對ASP.NET頁麵的自動授權,本文描述的解決方案是我剛剛想到的,希望廣大網友朋友們幫助評估一下。[源代碼從這裏下載]

目錄
一、實例演示
二、AuthorizationFilterAttribute
三、AuthorizeAttribute
四、PageBase

我們先來作一個簡單的實例演示。如下所示的EntLib安全模塊的配置,如果讀者對此不了解也沒有關係,在這裏我們隻需要關注定義其中的一個授權規則(Authorization Rule):“”。這是一個邏輯表達式,前綴I:和R:分別表示用戶名(Identity)和角色(Role),整個表達式表示的授權邏輯是:“帳號為Foo的用戶和所有具有Admin角色的用戶”有權限方法與此表達式關聯的操作或者資源。配置還定義了該授權規則的名稱“”。

   1: <configuration>
   2:   <configSections>
   3:     <section name="securityConfiguration" 
   4:              type="Microsoft.Practices.EnterpriseLibrary.Security.Configuration.SecuritySettings, 
   5:                    Microsoft.Practices.EnterpriseLibrary.Security"/>
   6:   </configSections>
   7:   <securityConfiguration defaultAuthorizationInstance="Authorization Rule Provider">
   8:     <authorizationProviders>
   9:       <add name="Authorization Rule Provider"
  10:            type="Microsoft.Practices.EnterpriseLibrary.Security.AuthorizationRuleProvider, 
  11:                  Microsoft.Practices.EnterpriseLibrary.Security">
  12:         <rules>
  13:           <add expression="I:Foo OR R:Admin" name="FooOrAdmin" />
  14:         </rules>
  15:       </add>
  16:     </authorizationProviders>
  17:   </securityConfiguration>
  18: </configuration>

我們添加一個需要授權的Web頁麵(Default.aspx),並且使用上麵定義的表達式來作為該頁麵的授權規則,我們通過自定義的AuthorizeAttribute特性實現兩者之間的關聯(該特性構造函數中指定的字符串正是配置的授權規則名稱)。除此之外,Web頁麵對應的類型繼承自我們自定義的基類PageBase。

   1: 
   2: public partial class Default : 
   3: {   
   4: }

我們隨後添加一個登錄頁麵,具體的實現就不再這裏一一介紹了。為了模擬不同的登錄用戶具有不同的權限,我們通過注冊HttpApplication的AuthenticateRequest事件來對當前Principal進行定製。具體的定義如下所示:如果用戶名為Bar,我們讓當前的Principal具有Admin角色,對於其他帳號的登錄用戶,角色列表為空。

   1: public class Global : System.Web.HttpApplication
   2: {
   3:     protected void Application_AuthenticateRequest(object sender, EventArgs e)
   4:     {
   5:         if (null == HttpContext.Current.User || !HttpContext.Current.User.Identity.IsAuthenticated)
   6:         {
   7:             return;
   8:         }
   9:  
  10:         IIdentity identity = new GenericIdentity(HttpContext.Current.User.Identity.Name);
  11:         string[] roles = null;
  12:         if (identity.Name.ToLower() == "bar")
  13:         {
  14:             roles = new string[] { "" };
  15:         }
  16:         HttpContext.Current.User = new GenericPrincipal(identity, roles);
  17:     }
  18: }

由於頁麵Default.aspx與配置名稱為FooOrAdmin的授權規則進行了關聯,根絕授權規則表達式定義和針對不同用戶的角色列表,意味著當我們以賬戶Foo和Bar登錄後才能訪問該頁麵,“自動化授權”可以通過下圖得到證實:當前用戶為Foo和Bar時,頁麵得以正常顯示;而當我們以Baz的身份登錄後,顯示“Access denied…”。

201210232132435570.png

這裏我吸取了ASP.NET MVC基於AuthorizationFilter的授權方式,不同的是AuthorizationFilter在ASP.NET MVC中以特性的方式應用到Controller類型和Action方法上,這裏我們則將它應用到Web頁麵對應的類上。AuthorizationFilterAttribute作為授權篩選器特性的基類定義如下,由於多個特性可以同時應用到同一個類型上,它們的執行順序通過屬性Order來控製。具體的授權判斷以及對非授權請求的處理定義在方法OnAuthorization方法上。

   1: [AttributeUsage( AttributeTargets.Class, AllowMultiple = true)]
   2: public abstract class AuthorizationFilterAttribute:Attribute
   3: {
   4:     public int Order { get; set; }
   5:     public abstract bool OnAuthorization(AuthorizationContext context);        
   6: }
   7:  
   8: public class AuthorizationContext
   9: {
  10:     public HttpContext  HttpContext { get; private set; }
  11:     public bool         UnAuthorizedRequestHandled { get; set; }
  12:  
  13:     public AuthorizationContext(HttpContext httpContext)
  14:     {
  15:         this.HttpContext = httpContext;
  16:     }
  17: }

OnAuthorization方法返回值代表的是真否授權的判斷,它具有一個類型為AuthorizationContext參數。AuthorizationContext是對HttpContext對象的封裝,屬性UnAuthorizedRequestHandled 表示是否完成了針對非授權請求的處理。如果多個AuthorizationFilterAttribute應用到同一個類型上,如果前麵執行的AuthorizationFilterAttribute將傳入的AuthorizationContext的這個屬性設置為True,後續的將不在執行。

基於EntLib的授權通過AuthorizeAttribute來實現。如下麵的代碼片斷所示,AuthorizeAttribute 直接繼承自AuthorizationFilterAttribute,代表授權規則配置名稱的屬性AuthorizationRule 在構造函數中被初始化。在實現的OnAuthorization我們按照Entlib授權框架的編程模式判斷當前Principal是否具有針對指定授權規則的權限,對於非授權請求我們直接調用HandleUnauthorizedRequest方法進行處理。具體的處理邏輯很簡單:直接相應一段文字“Access denied…”(正是上麵截圖中顯示的文字)。出於可擴展的考慮,我們將此方法定義成受保護的虛方法。

   1: [AttributeUsage( AttributeTargets.Class, AllowMultiple = true)]
   2: public class AuthorizeAttribute : AuthorizationFilterAttribute
   3: {
   4:     public string AuthorizationRule { get; private set; }
   5:     public AuthorizeAttribute(string authorizationRule)
   6:     {
   7:         Guard.ArgumentNotNullOrEmpty(authorizationRule, "authorizationRule");
   8:         this.AuthorizationRule = authorizationRule;
   9:     }
  10:  
  11:     public override bool OnAuthorization(AuthorizationContext context)
  12:     {
  13:         IAuthorizationProvider authorizationProvider = AuthorizationFactory.GetAuthorizationProvider();
  14:         if (authorizationProvider.Authorize(context.HttpContext.User, this.AuthorizationRule))
  15:         {
  16:             return true;
  17:         }
  18:         this.HandleUnauthorizedRequest(context);
  19:         return false;
  20:     }
  21:  
  22:     protected virtual void HandleUnauthorizedRequest(AuthorizationContext context)
  23:     {
  24:         context.HttpContext.Response.Write(context.HttpContext.User.Identity.Name + ": Access denied...");
  25:     }
  26: }

我們知道針對一個ASP.NET 資源的請求最後大都通過一個對應的HttpHandler來處理,這個授權解決方案的基本思路就是通過自定義HttpHandler實現自動化授權檢驗。Page類型是我們最為熟悉的HttpHandler,為此我們定義了如下一個繼承自它的類型PageBase。如下麵的代碼片斷所示,在重寫的ProcessRequest方法中實現了對應用在當前類型上的AuthorizationFilterAttribute特性的解析和執行,進而提供了對授權的實現。

   1: public abstract class PageBase: Page
   2: {
   3:     public override void ProcessRequest(HttpContext context)
   4:     {
   5:         var filterAttributes = this.GetType().GetCustomAttributes(true)
   6:                                 .OfType<AuthorizationFilterAttribute>()
   7:                                 .OrderBy(attribute => attribute.Order);
   8:         AuthorizationContext authorizationContext = new AuthorizationContext(context);
   9:         bool isAuthorized = true;
  10:         foreach (AuthorizationFilterAttribute attribute in filterAttributes)
  11:         {
  12:             isAuthorized = attribute.OnAuthorization(authorizationContext);
  13:             if (authorizationContext.UnAuthorizedRequestHandled)
  14:             {
  15:                 break;
  16:             }
  17:         }
  18:  
  19:         if (isAuthorized)
  20:         {
  21:             base.ProcessRequest(context);
  22:         }
  23:     }    
  24: }

作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-25 16:33:39

  上一篇:go  利用ASP.NET SiteMap生成與Bootstrap"兼容"菜單
  下一篇:go  無需寫try/catch,也能正常處理異常