利用EntLib授權機製實現對ASP.NET頁麵的自動授權
ASP.NET默認采用UrlAuthorizationModule和FileAuthorizationModule分別實現針對請求地址和物理文件的授權,但是在很多情況下我們需要額外的授權方式。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…”。
這裏我吸取了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