本文檔是Visual C# 教程 (轉至 Visual Basic 教程 ) 。
在本教程中,我們首先了解 Roles 框架如何將用戶(hù)的角色與其安全上下文相關(guān)聯(lián)。然后探討如何應用基于角色的URL 授權規則。最后,我們將討論如何通過(guò)聲明式和編碼,改變ASP.NET 網(wǎng)頁(yè)上的數據顯示和功能。
簡(jiǎn)介在《基于用戶(hù)的授權 》教程中,我們學(xué)習了如何使用URL 授權來(lái)規定哪些用戶(hù)能夠訪(fǎng)問(wèn)某個(gè)網(wǎng)頁(yè)集。只需在Web.config 中寫(xiě)上一些標記,我們就能夠讓ASP.NET 只允許經(jīng)過(guò)身份驗證的用戶(hù)訪(fǎng)問(wèn)一個(gè)網(wǎng)頁(yè)?;蛘呶覀兛梢砸幎?,只允許用戶(hù)Tito 和Bob 訪(fǎng)問(wèn);或規定,除Sam 以外的所有經(jīng)過(guò)身份驗證的用戶(hù)都允許訪(fǎng)問(wèn)。 除了 URL 授權之外,我們還探討了如何通過(guò)聲明和編碼,基于訪(fǎng)問(wèn)用戶(hù)來(lái)控制網(wǎng)頁(yè)上的數據顯示和功能。特別是,我們還創(chuàng )建了一個(gè)網(wǎng)頁(yè),在上面列出了當前目錄的內容。所有人都可以訪(fǎng)問(wèn)該網(wǎng)頁(yè),但只有經(jīng)過(guò)身份驗證的用戶(hù)能夠看到文件的內容,而只有Tito 能夠刪除文件。 按逐個(gè)用戶(hù)應用授權規則,會(huì )讓人墜入一場(chǎng)記帳式的惡夢(mèng)中。一個(gè)更便于維護的方法是使用基于角色的授權。令人欣慰的是,我們所配備的應用授權規則的工具,無(wú)論是針對角色還是針對用戶(hù)帳戶(hù),都可以良好地工作。URL 授權規則可以指定角色而不是用戶(hù)。至于LoginView 控件,它能向經(jīng)過(guò)身份驗證的用戶(hù)和匿名用戶(hù)顯示不同的網(wǎng)頁(yè)內容,也能設置為基于登錄用戶(hù)的角色顯示不同的內容。而Roles API 中包括確定登錄用戶(hù)角色的方法。 在本教程中,我們首先了解 Roles 框架如何將用戶(hù)的角色與其安全上下文相關(guān)聯(lián)。然后探討如何應用基于角色的URL 授權規則。最后,我們將討論如何通過(guò)聲明和編碼,改變ASP.NET 網(wǎng)頁(yè)上的數據顯示和功能。 讓我們開(kāi)始吧! 了解角色如何與用戶(hù)的安全上下文相關(guān)聯(lián)每當一個(gè)請求進(jìn)入ASP.NET 管道,它便關(guān)聯(lián)上一個(gè)安全上下文,其中包括請求者的身份信息。當使用表單身份驗證時(shí),用一個(gè)身份驗證票證作為身份標記。正如我們在《 表單身份驗證概述 》和《 表單身份驗證配置和高級主題 》教程中所討論過(guò)的,是在AuthenticateRequest 事件 中,由FormsAuthenticationModule 負責確定請求者的身份。 如果找到了一個(gè)有效的、沒(méi)有過(guò)期的身份驗證票證,FormsAuthenticationModule 則對其解碼,確定請求者的身份。它創(chuàng )建一個(gè)新的GenericPrincipal 對象并將其分配到 HttpContext.User 對象。像 GenericPrincipal 這樣的主體(principal )的目的,是識別經(jīng)過(guò)身份驗證的用戶(hù)的用戶(hù)名和他所屬的角色。該目的是很顯然的,因為所有主體對象都有一個(gè)Identity 屬性和一個(gè) IsInRole(roleName) 方法。但是,FormsAuthenticationModule 卻不記錄角色信息,而且它所創(chuàng )建的 GenericPrincipal 對象也不指定任何角色。 如果啟用了 Roles 框架,在FormsAuthenticationModule 之后就會(huì )進(jìn)入 RoleManagerModule HTTP Module ,并在 PostAuthenticateRequest 事件 中識別經(jīng)過(guò)身份驗證的用戶(hù)的角色,上述事件在A(yíng)uthenticateRequest 事件之后激發(fā)。如果該請求是由一個(gè)經(jīng)過(guò)身份驗證的用戶(hù)發(fā)出,則RoleManagerModule 覆蓋由 FormsAuthenticationModule 所創(chuàng )建的 GenericPrincipal 對象,用一個(gè)RolePrincipal 對象 替換它。RolePrincipal 類(lèi)使用Roles API 確定該用戶(hù)屬于哪些角色。 圖 1 說(shuō)明了使用表單身份驗證和Roles 框架時(shí)的 ASP.NET 管道流程。首先執行的是 FormsAuthenticationModule ,通過(guò)身份驗證票證識別用戶(hù),并創(chuàng )建一個(gè)新的GenericPrincipal 對象。然后,進(jìn)入RoleManagerModule ,并用 RolePrincipal 對象覆蓋 GenericPrincipal 對象。 如果一個(gè)匿名用戶(hù)訪(fǎng)問(wèn)網(wǎng)站,無(wú)論FormsAuthenticationModule 還是 RoleManagerModule 都不創(chuàng )建一個(gè)主體對象。 ![]() 圖1 :使用表單身份驗證和 Roles 框架時(shí),對經(jīng)過(guò)身份驗證的用戶(hù)的 ASP.NET 管道事件(單擊此處查看實(shí)際大小的圖像 ) 在Cookie 中緩存角色信息RolePrincipal 對象的IsInRole(roleName) 方法調用 Roles.GetRolesForUser 獲取用戶(hù)角色,以確定該用戶(hù)是否是roleName 的成員。當使用 SqlRoleProvider 時(shí),這引出一個(gè)到角色數據庫的查詢(xún)。當使用基于角色的URL 授權規則時(shí),對于受基于角色的 URL 授權規則保護的網(wǎng)頁(yè)的每次訪(fǎng)問(wèn)請求,都調用RolePrincipal 的 IsInRole 方法。但并不需要在每次請求時(shí)都到數據庫去查閱角色信息,Roles 框架有一個(gè)選項,將用戶(hù)角色存到一個(gè)cookie 里。 如果 Roles 框架配置為將用戶(hù)角色緩存到 cookie 里,RoleManagerModule 就在 ASP.NET 管道的 EndRequest 事件 期間創(chuàng )建該 cookie 。當RolePrincipal 對象創(chuàng )建后,PostAuthenticateRequest 請求中也使用該 cookie 。如果該 cookie 有效且沒(méi)有過(guò)期,cookie 里的數據就被解析并用來(lái)顯示用戶(hù)角色,不需要讓RolePrincipal 通過(guò)調用Roles 類(lèi)來(lái)確定用戶(hù)角色。圖 2 說(shuō)明了該流程。 ![]() 圖2 :在 Cookie 中存儲用戶(hù)角色信息以提高效率(單擊此處查看實(shí)際大小的圖像 ) 在默認狀態(tài)下,并不啟用角色緩存的cookie 機制??梢酝ㄟ^(guò)Web.config 中的<roleManager> 配置標記將其啟用。在《 創(chuàng )建和管理角色 》教程中,我們討論過(guò)如何使用<roleManager> 元素 指定角色提供者,所以在您的應用程序的Web.config 文件中應該已經(jīng)有該元素。將角色緩存cookie 配置指定為<roleManager> 元素的屬性,表1 對此做了一個(gè)總結。 注意 : 表1 列出的配置,是角色緩存 cookie 的屬性。如果想了解有關(guān) cookies 的更多信息,了解它們是如何工作的以及其各種屬性,請參閱Cookies 教程 。
表1 : 角色緩存Cookie 的配置選項 配置我們的應用程序為使用不持久性的角色緩存cookies 。為此,在 Web.config 中修改 <roleManager> 元素,包括下列與 cookie 有關(guān)的屬性: 我是這樣修改 <roleManager> 元素的,在其中加了三個(gè)屬性:cacheRolesInCookie 、createPersistentCookie 和 cookieProtection 。設置cacheRolesInCookie 為 true ,RoleManagerModule 就會(huì )自動(dòng)地將用戶(hù)角色緩存到一個(gè)cookie 中,而不用在每次請求時(shí)都去查看用戶(hù)角色信息。我明確地分別將createPersistentCookie 和 cookieProtection 屬性設為 false 和All 。其實(shí),從技術(shù)上說(shuō)我并不需要指定這些屬性的值,因為在這里設的都是它們的默認值。但是我將它們寫(xiě)在上面,是為了清楚地表明我不使用持久性cookie ,而且 cookie 是經(jīng)過(guò)加密的、有效的。 這樣就可以了 !從此以后,Roles 框架將將用戶(hù)角色緩存到 cookie 中。如果用戶(hù)瀏覽器不支持 cookie ,或他們的 cookie 因為某種原因被刪除了或丟失了,這也不要緊,在沒(méi)有cookie 可用的情況下(失效或過(guò)期了),RolePrincipal 對象會(huì )去使用 Roles 類(lèi)。 注意 :Microsoft 的Patterns & Practices 小組不提倡使用持久性角色緩存 cookie 。因為擁有了角色緩存 cookie ,就足以證明成員資格。如果黑客用某種方法獲得了某用戶(hù)有效的cookie 數據,他就可以模仿那個(gè)用戶(hù)。如果用戶(hù)瀏覽器的cookie 是持久性的,則增加了發(fā)生這種事情的可能性。有關(guān)該安全性建議的更多信息,以及其他安全性?xún)热?,請參?a target="_blank" > Security Question List for ASP.NET 2.0 。 步驟1 :定義基于角色的URL 授權規則正如我們在《 基于用戶(hù)的授權 》教程中所學(xué)過(guò)的,URL 授權提供了一種手段,基于逐個(gè)用戶(hù)或基于逐個(gè)角色,對一個(gè)網(wǎng)頁(yè)集的訪(fǎng)問(wèn)做出限制。在Web.config 中,用<authorization> 元素 的<allow> 和<deny> 子元素,規定URL 授權規則。除了在以前教程中討論過(guò)的基于用戶(hù)的授權規則之外,每個(gè)<allow> 和<deny> 子元素還可以包括:
例如,URL 授權規則批準擔任 Administrators 和 Supervisors 角色的那些用戶(hù)訪(fǎng)問(wèn),但拒絕所有其他用戶(hù)訪(fǎng)問(wèn): 以上標記中的 <allow> 元素表示,Administrators 和Supervisors 角色是允許的;而 <deny> 元素則指明,所有用戶(hù)都是不允許的。 讓我們配置我們的應用程序,讓ManageRoles.aspx 、UsersAndRoles.aspx 和CreateUserWizardWithRoles.aspx 頁(yè)都只允許擔任 Administrators 角色的那些用戶(hù)訪(fǎng)問(wèn),而RoleBasedAuthorization.aspx 頁(yè)則保留允許所有訪(fǎng)問(wèn)者進(jìn)入。 為此,首先將一個(gè) Web.config 文件加到Roles 文件夾中。 ![]() 圖3 :加入一個(gè) Web.config 文件到 Roles 目錄(單擊此處查看實(shí)際大小的圖像 ) 然后,添加以下配置標記到 Web.config 中: 在 <system.web> 段落中的<authorization> 元素表示,只有擔任 Administrators 角色的用戶(hù)能夠訪(fǎng)問(wèn) Roles 目錄下的ASP.NET 資源。<location> 元素定義了對 RoleBasedAuthorization.aspx 頁(yè)的URL 授權規則的一個(gè)替換設置,允許所有用戶(hù)訪(fǎng)問(wèn)此頁(yè)。 在保存了在 Web.config 上所做的這些改動(dòng)之后,以一個(gè)不是Administrators 角色的用戶(hù)身份登錄,然后試著(zhù)訪(fǎng)問(wèn)一個(gè)這些被保護的頁(yè)面。UrlAuthorizationModule 馬上探測出,您沒(méi)有獲得對所請求資源的訪(fǎng)問(wèn)許可,因此,FormsAuthenticationModule 將將您重新引到登錄頁(yè)面。然后,登錄頁(yè)面將您再引到UnauthorizedAccess.aspx 頁(yè)(見(jiàn)圖4 )。發(fā)生最后該從登錄頁(yè)到UnauthorizedAccess.aspx 頁(yè)的引導,是因為我們在《 | 基于用戶(hù)的授權 》教程的步驟 2 里,在登錄頁(yè)里加入了代碼。特別是,如果查詢(xún)字符串中包含ReturnUrl 參數,登錄頁(yè)就自動(dòng)將任何經(jīng)身份驗證的用戶(hù)引導到UnauthorizedAccess.aspx 頁(yè),因為該參數表示用戶(hù)是在試圖訪(fǎng)問(wèn)一個(gè)沒(méi)有向他授權的網(wǎng)頁(yè)后抵達登錄頁(yè)的。 ![]() 圖4 :只有擔任 Administrators 角色的用戶(hù)能夠查閱被保護的網(wǎng)頁(yè)(單擊此處查看實(shí)際大小的圖像 ) 現在,退出登錄再重新以一個(gè)擔任Administrators 角色的用戶(hù)登錄?,F在,您應該能夠查閱三個(gè)被保護的網(wǎng)頁(yè)了。 ![]() 圖5 :Tito 能夠訪(fǎng)問(wèn) UsersAndRoles.aspx 頁(yè)是因為他擔任著(zhù) Administrators 角色(單擊此處查看實(shí)際大小的圖像 ) 注意 : 在規定URL 授權規則時(shí),無(wú)論是對角色還是對用戶(hù),要記住的很重要的一點(diǎn)是,規則是從上至下逐一分析的。一旦發(fā)現一個(gè)匹配,用戶(hù)就被批準或拒絕訪(fǎng)問(wèn),取決于匹配是在<allow> 還是在<deny> 元素中找到的。如果沒(méi)有找到匹配,就批準用戶(hù)訪(fǎng)問(wèn)。因此,如果您想限制一個(gè)或多個(gè)用戶(hù)帳戶(hù)的訪(fǎng)問(wèn),就必須要用一個(gè)<deny> 元素作URL 授權配置的最后一個(gè)元素。如果您的URL 授權規則中沒(méi)有包括一個(gè)<deny> 元素,就相當于批準所有用戶(hù)訪(fǎng)問(wèn)。 有關(guān)如何分析URL 授權規則的更全面的討論,請復習《 基于用戶(hù)的授權 》教程中的“UrlAuthorizationModule 如何使用授權規則批準或拒絕訪(fǎng)問(wèn)”一節。 步驟2 :基于當前登錄用戶(hù)的角色限制頁(yè)面功能有了 URL 授權規則,就很容易制定一個(gè)粗略的授權規則,允許或拒絕哪些身份的用戶(hù)訪(fǎng)問(wèn)某一特定的網(wǎng)頁(yè)(或一個(gè)文件夾及其子文件夾里的所有網(wǎng)頁(yè))。但在某些情況下,我們想允許所有用戶(hù)訪(fǎng)問(wèn)一個(gè)網(wǎng)頁(yè),但想基于訪(fǎng)問(wèn)用戶(hù)的角色限制網(wǎng)頁(yè)上的功能。這就需要基于用戶(hù)的角色顯示或隱藏數據,或對那些屬于某一特定角色的用戶(hù)提供附加的功能。 這種精細的基于角色的授權規則,可以通過(guò)聲明或編碼(或兩者的結合)實(shí)現。下面一節,我們就將看看如何通過(guò)LoginView 控件制定聲明方式的精細授權。再往后,我們接著(zhù)探討通過(guò)編碼完成此項任務(wù)的技術(shù)。但是,在我們考察如何制定精細授權規則之前,我們首先需要創(chuàng )建一個(gè)網(wǎng)頁(yè),其功能依賴(lài)于訪(fǎng)問(wèn)其用戶(hù)的角色。 我們創(chuàng )建一個(gè)網(wǎng)頁(yè),它在一個(gè) GridView 中列出系統中所有的用戶(hù)帳戶(hù)。GridView 中將包括每個(gè)用戶(hù)的用戶(hù)名、郵箱地址、上次登錄日期和對該用戶(hù)的評論。除了顯示每個(gè)用戶(hù)的信息之外,該GridView 還包括編輯和刪除功能。我們最初創(chuàng )建該網(wǎng)頁(yè)時(shí),允許所有用戶(hù)使用編輯和刪除功能。在“使用LoginView 控件”和“通過(guò)編碼限制功能”兩節中,我們將會(huì )看到如何基于訪(fǎng)問(wèn)用戶(hù)的角色,啟用或不啟用這些功能。 注意 : 我們準備創(chuàng )建的ASP.NET 網(wǎng)頁(yè)使用一個(gè) GridView 控件列出用戶(hù)帳戶(hù)。由于本系列教程集中討論表單身份驗證、授權、用戶(hù)帳戶(hù)和角色,我們不想用太多時(shí)間討論GridView 控件的內部功能。盡管本教程詳細提供了設置該網(wǎng)頁(yè)的一步步的指導,但我們并不探究為什么要做出某些選擇,或某些特定屬性將呈現什么輸出效果等細節。對GridView 控件的更全面的說(shuō)明,請參閱我的Working with Data in ASP.NET 2.0 系列教程。 首先,在 Roles 文件夾中打開(kāi)RoleBasedAuthorization.aspx 網(wǎng)頁(yè)。將一個(gè) GridView 拖到網(wǎng)頁(yè)的 Designer 上并將其ID 設置為 UserGrid 。一會(huì )兒我們將寫(xiě)代碼調用 Membership.GetAllUsers 方法,并將產(chǎn)生的 MembershipUserCollection 對象綁定到該 GridView 。MembershipUserCollection 對系統中的每個(gè)用戶(hù)帳戶(hù)包含一個(gè) MembershipUser 對象;而 MembershipUser 對象具有屬性 UserName 、Email 、LastLoginDate 等。 在我們寫(xiě)代碼綁定用戶(hù)帳戶(hù)到該表格之前,我們先定義GridView 的列。在 GridView 的智能標記中,單擊 Edit Columns 鏈接,調出一個(gè) Fields 對話(huà)框(見(jiàn)圖 6 )。在對話(huà)框的左下角,不勾選 Auto-generate fields 復選框。因為我們想要該 GridView 包括編輯和刪除功能,加入一個(gè)CommandField ,并設置其 ShowEditButton 和 ShowDeleteButton 屬性為T(mén)rue 。然后,加入四個(gè)列分別顯示 UserName 、Email 、LastLoginDate 和 Comment 屬性。對兩個(gè)只讀屬性(UserName 和LastLoginDate )使用 BoundField ,對兩個(gè)可編輯列(Email 和 Comment )使用 TemplateField 。 用第一個(gè) BoundField 顯示UserName 屬性,設置其 HeaderText 和 DataField 屬性為 “UserName” 。該列是不能編輯的,所以設置其 ReadOnly 屬性為 True 。再配置 LastLoginDate BoundField ,將其 HeaderText 設為 “Last Login” ,其DataField 設為 “LastLoginDate” 。我們還要規定一下該 BoundField 的輸出格式,讓其只顯示日期(而不是顯示日期和時(shí)間)。為此,設置該BoundField 的 HtmlEncode 屬性為 False ,并且設其DataFormatString 屬性為 “{0:d}” 。ReadOnly 屬性也設為 True 。 設置其他兩個(gè) TemplateField 的HeaderText 屬性為 “Email” 和 “Comment” 。 ![]() 圖6 :通過(guò) Fields 對話(huà)框配置 GridView 的列(單擊此處查看實(shí)際大小的圖像 ) 我們現在需要為 “Email” 和“Comment” 的 TemplateField 定義 ItemTemplate 和 EditItemTemplate 。添加一個(gè)Web 標簽控件到每個(gè) ItemTemplate 上,并將它們的 Text 屬性分別綁定到 Email 和 Comment 屬性。 對于 “Email” TemplateField ,添加一個(gè)名為Email 的 TextBox 到其 EditItemTemplate ,并使用雙向數據綁定,將其 Text 屬性綁定到 Email 屬性。添加一個(gè)RequiredFieldValidator 和一個(gè) RegularValidator 到EditItemTemplate ,以保證在編輯 Email 屬性時(shí)輸入正確格式的郵箱地址。對于“Comment” TemplateField ,添加一個(gè)名為 Comment 的多行 TextBox 到其 EditItemTemplate 。設置 TextBox 的Columns 和 Rows 屬性分別為40 和 4 ,然后用雙向數據綁定將其 Text 屬性綁定到 Comment 屬性。 在配置完這些 TemplateField 之后,它們的聲明標記應該如下所示: 在編輯或刪除一個(gè)用戶(hù)帳戶(hù)時(shí),我們需要知道用戶(hù)的UserName 屬性值。設置 GridView 的 DataKeyNames 屬性為 “UserName” ,這樣,通過(guò) GridView 的DataKeys 集合就可獲得該信息。 最后 , 在頁(yè)面上添加一個(gè) ValidationSummary 控件 , 將其 ShowMessageBox 屬性設為 True ,ShowSummary 屬性設為 False 。 做了這些設置后,當操作者編輯用戶(hù)帳戶(hù)時(shí)沒(méi)有輸入或輸入了不正確的郵箱地址后,ValidationSummary 就會(huì )彈出一個(gè)客戶(hù)端警告信息。 我們現在已經(jīng)完成了本頁(yè)的聲明標記。下一步的任務(wù)是綁定用戶(hù)帳戶(hù)集合到GridView 。加入一個(gè)名為 BindUserGrid 的方法到 RoleBasedAuthorization.aspx 頁(yè)的代碼文件類(lèi),它綁定由 Membership.GetAllUsers 返回的 MembershipUserCollection 到 UserGrid GridView 。在第一次打開(kāi)網(wǎng)頁(yè)時(shí)的 Page_Load 事件處理程序中調用該方法。 放置好這些代碼之后,通過(guò)瀏覽器訪(fǎng)問(wèn)該網(wǎng)頁(yè)。如圖7 所示,在一個(gè) GridView 中列出了系統中每個(gè)用戶(hù)的信息。 ![]() 圖7 :UserGrid GridView 列出系統中每個(gè)用戶(hù)的信息(單擊此處查看實(shí)際大小的圖像 ) 注意 :UserGrid GridView 是在一個(gè)不能分頁(yè)的界面上列出所有用戶(hù)信息的。這種簡(jiǎn)單的表格界面并不適于有幾十個(gè)或更多用戶(hù)的場(chǎng)合??蛇x方法之一是設置GridView 為可分頁(yè)的。Membership.GetAllUsers 方法有兩個(gè)重載:一個(gè)不接收輸入參數返回所有用戶(hù),另一個(gè)接收頁(yè)面索引和頁(yè)面大小的整數值,僅返回一個(gè)特定的用戶(hù)子集。使用第二個(gè)重載可以更高效地翻頁(yè)查看用戶(hù),因為返回的只是適合顯示的用戶(hù)帳戶(hù)的子集,而不是全體用戶(hù)。如果您有幾千個(gè)用戶(hù)帳戶(hù),就可能需要考慮使用篩選器形式的界面了。例如,只顯示UserName 由選定字母打頭的那些用戶(hù)。Membership.FindUsersByName方法 用于建立篩選器形式的用戶(hù)界面是很方便的。在以后的教程中,我們將探討如何建立這樣的界面。 在綁定到一個(gè)配置完好的數據源控件如SqlDataSource 或 ObjectDataSource 后,GridView 控件提供了內置的編輯和刪除功能。但是,UserGrid GridView 的數據是通過(guò)編碼綁定的,所以我們必須寫(xiě)代碼來(lái)執行這兩項任務(wù)。特別是,我們需要為GridView 的 RowEditing 、RowCancelingEdit 、RowUpdating 和RowDeleting 事件創(chuàng )建事件處理程序,這些事件是在訪(fǎng)問(wèn)者單擊了GridView 的 Edit 、Cancel 、Update 或 Delete 按鈕后引發(fā)的。 首先為 GridView 的RowEditing 、RowCancelingEdit 和RowUpdating 事件創(chuàng )建事件處理程序,然后添加以下代碼: RowEditing 和RowCancelingEdit 事件處理程序就是設置 GridView 的EditIndex 屬性,然后重新綁定這些用戶(hù)帳戶(hù)到該表格。更多的內容在RowUpdating 事件處理程序里。該事件處理程序首先確認數據是有效的,然后從DataKeys 集合中獲取被編輯的用戶(hù)帳戶(hù)的 UserName 值。然后通過(guò)編碼定位兩個(gè) TemplateField 上 EditItemTemplate 中的 Email 和Comment 文本框。它們的 Text 屬性中包含編輯完的郵箱地址和評論。 為了用 Membership API 更新用戶(hù)帳戶(hù),我們需要首先獲得用戶(hù)的信息,這通過(guò)調用Membership.GetUser(userName) 來(lái)實(shí)現。然后,返回的 MembershipUser 對象的Email 和 Comment 屬性由編輯界面中輸入到兩個(gè)文本框里的值所更新。最后,通過(guò)調用Membership.UpdateUser 保存所做的這些修改。RowUpdating 事件處理程序的最后,將 GridView 轉回到它編輯前的狀態(tài)。 下一步,創(chuàng )建 RowDeleting 事件處理程序,并加入以下代碼: 以上事件處理程序首先從 GridView 的DataKeys 集合獲取 UserName 的值,然后,將該 UserName 值送到 Membership 類(lèi)的 DeleteUser 方法 。DeleteUser 方法從系統中刪除用戶(hù)帳戶(hù),包括相應的成員數據(例如該用戶(hù)屬于哪些角色)。刪除完用戶(hù)之后,表格的EditIndex 設為 -1 (考慮到這種情況:當用戶(hù)單擊 Delete 按鈕時(shí),另一行正處在編輯狀態(tài)),調用BindUserGrid 方法。 注意 : 這的Delete 按鈕在刪除用戶(hù)帳戶(hù)前沒(méi)有要求用戶(hù)做任何確認。我建議您還是加上某些方式的用戶(hù)確認,以減少帳戶(hù)被誤刪的危險。一個(gè)最簡(jiǎn)單的操作確認方式就是通過(guò)一個(gè)客戶(hù)端確認對話(huà)框。有關(guān)這項技術(shù)的更多信息,參閱《刪除時(shí)添加客戶(hù)端確認 》 。 讓我們驗證一下本頁(yè)的功能。您現在應該能夠編輯任何一個(gè)用戶(hù)的郵箱地址和評論,也能夠刪除任何一個(gè)用戶(hù)帳戶(hù)。因為所有用戶(hù)都可以訪(fǎng)問(wèn)到RoleBasedAuthorization.aspx 頁(yè),所以現在任何一個(gè)用戶(hù)(甚至匿名用戶(hù))都能夠訪(fǎng)問(wèn)本頁(yè),并能夠編輯和刪除用戶(hù)帳戶(hù)!我們下面準備修改該網(wǎng)頁(yè),讓只有擔任Supervisors 和 Administrators 角色的用戶(hù)能夠編輯用戶(hù)的郵箱地址和評論,而只有Administrators 能夠刪除用戶(hù)帳戶(hù)。 在“使用 LoginView 控件”這節里,我們將考察如何使用 LoginView 控件專(zhuān)門(mén)針對用戶(hù)角色顯示說(shuō)明。如果一個(gè)有著(zhù)Administrators 角色的人訪(fǎng)問(wèn)該網(wǎng)頁(yè),我們就顯示有關(guān)如何編輯和刪除用戶(hù)的說(shuō)明。如果一個(gè)擔任Supervisors 角色的用戶(hù)到了該網(wǎng)頁(yè),我們就向他顯示編輯用戶(hù)信息的說(shuō)明。但如果訪(fǎng)問(wèn)者是匿名用戶(hù),或既不是Supervisors 也不是 Administrators 角色,我們就向他顯示為何不能編輯或刪除用戶(hù)的解釋信息。在“通過(guò)編碼限制功能”一節,我們將寫(xiě)代碼,根據用戶(hù)的角色通過(guò)編碼顯示或隱藏Edit 和 Delete 按鈕。 使用LoginView 控件我們在以前的教程中學(xué)過(guò),使用LoginView 控件可以針對經(jīng)身份驗證的用戶(hù)和匿名用戶(hù)顯示不同的界面。其實(shí),LoginView 控件還可以用來(lái)針對用戶(hù)角色顯示不同的界面。我們現在根據訪(fǎng)問(wèn)用戶(hù)的角色,使用LoginView 控件顯示不同的說(shuō)明。 首先在 UserGrid GridView 上面添加一個(gè)LoginView 。我們過(guò)去學(xué)過(guò),LoginView 控件有兩個(gè)內置的模板:AnonymousTemplate 和 LoggedInTemplate 。在這兩個(gè)模板中都輸入一段簡(jiǎn)短的信息,通知用戶(hù)他們不能編輯或刪除任何用戶(hù)信息。 除了 AnonymousTemplate 和LoggedInTemplate 之外,LoginView 控件還可以包括一些 RoleGroup ,這是特別針對角色的模板。每個(gè) RoleGroup 包含一個(gè)單一的屬性 Roles ,它指定 RoleGroup 用于哪些角色。Roles 屬性可以設為單個(gè)角色(如 ”Administrators” ),或是以逗號隔開(kāi)的一組角色(如“Administrators, Supervisors” )。 為管理這些 RoleGroup ,從控件的智能標記中單擊 Edit RoleGroups 鏈接,打開(kāi) RoleGroup Collection Editor 。添加兩個(gè)新的 RoleGroup 。設置第一個(gè) RoleGroup 的 Roles 屬性為 “Administrators” ,設置第二個(gè)為 “Supervisors” 。 ![]() 圖8 :通過(guò) RoleGroup Collection Editor 管理 LoginView 的角色模板(單擊此處查看實(shí)際大小的圖像 ) 單擊 OK 關(guān)閉RoleGroup Collection Editor 。這也就修改了LoginView 的聲明標記,使之包括一個(gè)<RoleGroups> 段落,其中在 RoleGroup Collection Editor 中定義的每個(gè)RoleGroup 都帶一個(gè) <asp:RoleGroup> 子元素。此外,在 LoginView 智能標記上的 Views 下拉列表里,最初只包括AnonymousTemplate 和 LoggedInTemplate ,現在有了新加入的 RoleGroups 。 編輯這兩個(gè) RoleGroup ,使頁(yè)面上對擔任Supervisors 角色的用戶(hù)顯示如何編輯用戶(hù)帳戶(hù)的說(shuō)明,對擔任Administrators 角色的用戶(hù)顯示編輯和刪除的說(shuō)明。 做了這些改變后 , LoginView 的聲明標記應如下所示。 做完這些修改之后,保存網(wǎng)頁(yè)并通過(guò)瀏覽器訪(fǎng)問(wèn)它。第一次作為一個(gè)匿名用戶(hù)訪(fǎng)問(wèn)。您應該看到的信息是:“You are not logged into the system.Therefore you cannot edit or delete any user information.” (“您還沒(méi)有登錄。所以您不能編輯或刪除任何用戶(hù)信息?!保┤缓?,再用一個(gè)經(jīng)過(guò)身份驗證的用戶(hù)登錄,但既不是Supervisors 也不是 Administrators 角色。這次,您應該讀到的信息是:“You are not a member of the Supervisors or Administrators roles.Therefore you cannot edit or delete any user information.” (“您不是 Supervisors 或Administrators 角色的成員,所以您不能編輯或刪除任何用戶(hù)信息?!保?/p> 下一步,以一個(gè)擔任 Supervisors 角色的用戶(hù)身份登錄。這次您看到的應該是專(zhuān)門(mén)對Supervisors 角色的說(shuō)明信息(見(jiàn)圖 9 )。而如果您以一個(gè) Administrators 角色的用戶(hù)登錄,您應該看到的是專(zhuān)門(mén)對Administrators 角色的說(shuō)明信息(見(jiàn)圖 10 )。 ![]() 圖9 :向 Bruce 顯示專(zhuān)門(mén)對 Supervisors 角色的說(shuō)明信息(單擊此處查看實(shí)際大小的圖像 ) ![]() 圖10 :向 Tito 顯示專(zhuān)門(mén)對 Administrators 角色的說(shuō)明信息(單擊此處查看實(shí)際大小的圖像) 從圖 9 和圖10 所示的截屏畫(huà)面上看,LoginView 只呈現一個(gè)模板,即使應用到了多個(gè)模板。Bruce 和 Tito 都是以用戶(hù)身份登錄的,但 LoginView 只呈現了相匹配的 RoleGroup 而不是 LoggedInTemplate 。再有,Tito 屬于 Administrators 和 Supervisors 兩種角色,但LoginView 控件呈現的是專(zhuān)門(mén)對 Administrators 角色的模板而不是對 Supervisors 角色的。 圖 11 說(shuō)明了LoginView 控件所使用的流程,決定呈現哪個(gè)模板。注意,如果不只指定了一個(gè)RoleGroup ,LoginView 模板呈現第一個(gè)相匹配的 RoleGroup 。換句話(huà)說(shuō),如果我們放置 Supervisors RoleGroup 作為第一個(gè) RoleGroup ,而 Administrators 作為第二個(gè),則當Tito 訪(fǎng)問(wèn)該頁(yè)面時(shí),他看到的會(huì )是對 Supervisors 的信息。 ![]() 圖11 :LoginView 控件決定呈現哪個(gè)模板的流程(單擊此處查看實(shí)際大小的圖像 ) 通過(guò)編碼限制功能盡管 LoginView 控件基于訪(fǎng)問(wèn)頁(yè)面用戶(hù)的角色顯示了不同的說(shuō)明,但所有人都仍然可以看到Edit 和 Cancel 按鈕。我們要通過(guò)編碼,對匿名用戶(hù)和既不是Supervisors 也不是 Administrators 角色的用戶(hù)隱藏 Edit 和Delete 按鈕。還要對所有不是 Administrator 的用戶(hù)隱藏 Delete 按鈕。為此,我們要寫(xiě)一些代碼,通過(guò)編碼定位CommandField 上的Edit 和 Delete 按鈕,并在需要時(shí)設置它們的 Visible 屬性為 false 。 最簡(jiǎn)單的辦法通過(guò)編碼定位一個(gè)CommandField 上的控件,就是先將該列轉化為一個(gè)模板。為此,在GridView 的智能標記上單擊 Edit Columns 鏈接,從當前列的列表中選擇該 CommandField ,并單擊Convert this field into a TemplateField 鏈接。這就將 CommandField 轉化為一個(gè) 帶ItemTemplate 和 EditItemTemplate 的 TemplateField 。該 ItemTemplate 包含Edit 和 Delete LinkButton ,而 EditItemTemplate 包含 Update 和Cancel LinkButton 。 ![]() 圖12 : 將 CommandField 轉換為 TemplateField (單擊此處查看實(shí)際大小的圖像 ) 修改 ItemTemplate 上的Edit 和 Delete LinkButton ,分別將其 ID 屬性設置為 EditButton 和 DeleteButton 。 每當將數據綁定到 GridView ,該GridView 就在其 DataSource 屬性中放置記錄,并生成一個(gè)相應的 GridViewRow 對象。在每個(gè)GridViewRow 對象創(chuàng )建時(shí),都引發(fā) RowCreated 事件。為了向未授權用戶(hù)隱藏 Edit 和 Delete 按鈕,我們需要為該事件創(chuàng )建一個(gè)事件處理程序,通過(guò)編碼定位Edit 和 Delete LinkButton ,再相應地設置它們的 Visible 屬性。 創(chuàng )建 RowCreated 事件的事件處理程序并加入以下代碼: 必須知道,對于 GridView 的所有行都引發(fā)RowCreated 事件,包括表頭、腳注和分頁(yè)標記等。如果我們是處理不在編輯模式的數據行(因為處在編輯模式的行上有Update 和 Cancel 按鈕,而不是 Edit 和 Delete 按鈕),則我們希望通過(guò)編碼定位的只是Edit 和 Delete 按鈕。該檢查是通過(guò) if 語(yǔ)句實(shí)現的。 如果我們是處理一個(gè)不在編輯模式的數據行,則定位Edit 和 Delete 按鈕后,根據 User 對象的 IsInRole(roleName) 所返回的布爾值,設置它們的 Visible 屬性。User 對象定位由RoleManagerModule 創(chuàng )建的主體,因此,IsInRole(roleName) 方法使用 Roles API 確定當前訪(fǎng)問(wèn)者是否屬于roleName 。 注意 : 我們也可以直接使用Roles 類(lèi),而不用通過(guò)調用Roles.IsUserInRole(roleName)方法 調用User.IsInRole(roleName) 。我決定在該例子中使用主體對象的 IsInRole(roleName) 方法,是因為它比直接使用 Roles API 效率更高。本教程前面我們曾討論過(guò)通過(guò)配置role manager 在 cookie 中緩存用戶(hù)角色。只有當調用主體的 IsInRole(roleName) 方法時(shí),才用到該緩存的 cookie 數據,而直接調用 Roles API 卻需要到庫里去取出存儲的角色數據。即使角色數據沒(méi)有緩存到cookie 中,調用主體對象的 IsInRole(roleName) 方法通常也效率更高,因為當在請求中第一次調用它時(shí),它將結果緩存。而Roles API 卻不做任何緩存。因為 GridView 的每行都引發(fā)一次 RowCreated 事件,使用 User.IsInRole(roleName) 只需要到庫里去取一次角色數據,而 Roles.IsUserInRole(roleName) 則需要到庫里去取 N 次,其中 N 是顯示在表格中的用戶(hù)帳戶(hù)數。 如果訪(fǎng)問(wèn)本頁(yè)的用戶(hù)屬于 Administrators 或Supervisors 角色,Edit 按鈕的 Visible 屬性設為 true ,否則設它為 false 。只有當用戶(hù)屬于 Administrators 角色時(shí),Delete 按鈕的 Visible 屬性才設為true 。 打開(kāi)瀏覽器測試此頁(yè)面。 如果您以一個(gè)匿名用戶(hù)身份或既不是 Supervisor 也不是 Administrator 的用戶(hù)身份訪(fǎng)問(wèn)本頁(yè),CommandField 便是空的;這一列仍然在那里,只是變成了一個(gè)窄條,沒(méi)有了Edit 和 Delete 按鈕。 注意 : 當非Supervisor 和非 Administrator 訪(fǎng)問(wèn)網(wǎng)頁(yè)時(shí),也能做到將 CommandField 整個(gè)隱藏起來(lái)。 我將此作為練習留給讀者完成。 ![]() 圖13 :Edit 和 Delete 按鈕對非 Supervisors 和非 Administrators 隱藏了起來(lái)(單擊此處查看實(shí)際大小的圖像 ) 如果訪(fǎng)問(wèn)用戶(hù)屬于 Supervisors 角色(但不屬于A(yíng)dministrators 角色),他就只能看到 Edit 按鈕。 ![]() 圖14 :Edit 按鈕對 Supervisors 開(kāi)放,而Delete 按鈕卻隱藏了起來(lái)(單擊此處查看實(shí)際大小的圖像 ) 而當一個(gè) Administrator 來(lái)訪(fǎng)問(wèn)時(shí),他既可以使用 Edit 按鈕,也可以使用 Delete 按鈕。 ![]() 圖15 :只對 Administrators 開(kāi)放 Edit 和 Delete 按鈕(單擊此處查看實(shí)際大小的圖像 ) 步驟3 :將基于角色的授權規則應用于類(lèi)和方法在步驟 2 中我們做了限制,使只有 Supervisors 和 Administrators 角色能夠使用編輯功能,而只有Administrators 能夠使用刪除功能。我們是用程序方式,對未授權用戶(hù)隱藏相應的用戶(hù)界面元素來(lái)做到這一點(diǎn)的。但這種方法并不能完全避免未授權用戶(hù)使用本應限制他們的操作。一些隱藏的界面元素可能后來(lái)又被加進(jìn)去了,而我們又忘記將它們對未授權用戶(hù)再隱藏起來(lái)?;蚴呛诳涂赡芡ㄟ^(guò)某些方法獲得ASP.NET 頁(yè),并執行他們想要的方法。 一個(gè)最簡(jiǎn)便的保證某個(gè)特定功能不對未授權用戶(hù)開(kāi)放的方法,是用PrincipalPermission 屬性 改寫(xiě)那個(gè)類(lèi)或方法。當 .NET runtime 使用一個(gè)類(lèi)或執行其一個(gè)方法時(shí),它要做檢查以確保當前的安全上下文是許可的。PrincipalPermission 屬性提供了一個(gè)機制,通過(guò)它我們可以定義這些規則。 在前面的《 基于用戶(hù)的授權 》教程中,我們曾學(xué)習過(guò)使用PrincipalPermission 屬性。具體來(lái)說(shuō),我們學(xué)習了如何改寫(xiě)GridView 的SelectedIndexChanged 和RowDeleting 事件處理程序,使它們只能夠被經(jīng)過(guò)身份驗證的用戶(hù)和Tito 執行。該PrincipalPermission 屬性對于角色也同樣適用。 現在,我們示范一下在 GridView 的RowUpdating 和 RowDeleting 事件處理程序中使用 PrincipalPermission 屬性,禁止未授權用戶(hù)執行。我們所需要做的,就是在每個(gè)功能定義的上面加上適當的屬性: RowUpdating 事件處理程序規定,只有擔任 Administrators 或 Supervisors 角色的用戶(hù)能夠執行事件處理程序。而RowDeleting 事件處理程序的屬性限定,只有擔任 Administrators 角色的用戶(hù)才能執行它。 注意 :PrincipalPermission 屬性是作為一個(gè)類(lèi)在System.Security.Permissions 命名空間中出現的。確保要在代碼文件類(lèi)文件的頂部加上一條使用System.Security.Permissions 的語(yǔ)句以導入該命名空間。 如果不知何故一個(gè)非 Administrator 用戶(hù)試圖執行RowDeleting 事件處理程序,或是一個(gè)非 Supervisor 及非 Administrator 用戶(hù)試圖執行 RowUpdating 事件處理程序的話(huà),.NET runtime 將發(fā)出 SecurityException 異常信息。 ![]() 圖16 :如果安全上下文沒(méi)有授權執行方法,則發(fā)出SecurityException 異常信息(單擊此處查看實(shí)際大小的圖像 ) 除了 ASP.NET 網(wǎng)頁(yè)外,很多應用程序還具有包括各種層的架構,如業(yè)務(wù)邏輯層和數據訪(fǎng)問(wèn)層。這些層所起的作用就像Class Libraries 一樣,提供類(lèi)和方法,執行與業(yè)務(wù)邏輯和數據訪(fǎng)問(wèn)相關(guān)的功能。PrincipalPermission 屬性也同樣可以對于這些層應用授權規則。 有關(guān)使用 PrincipalPermission 屬性對類(lèi)和方法定義授權規則的更多信息,參閱Scott Guthrie 的博客文章使用 PrincipalPermissionAttributes 為業(yè)務(wù)或數據層添加授權規則 。 小結在本教程中,我們探討了如何基于用戶(hù)角色粗略地或精細地制定授權規則。利用ASP.NET 的URL 授權特性,網(wǎng)站開(kāi)發(fā)人員可以設定,允許或拒絕哪些身份的用戶(hù)訪(fǎng)問(wèn)哪些網(wǎng)頁(yè)。我們在過(guò)去的《 基于用戶(hù)的授權 》教程中學(xué)過(guò),URL 授權規則可以用在逐個(gè)用戶(hù)的基礎上。它們也可以用在逐個(gè)角色的基礎上,正如我們在本教程的步驟1 中所討論的。 應用精細授權規則,可以通過(guò)聲明標記,也可以通過(guò)編碼。在步驟2 中,我們考察了使用 LoginView 控件的 RoleGroups 特性,基于訪(fǎng)問(wèn)用戶(hù)的角色在網(wǎng)頁(yè)上呈現不同的輸出結果。我們還探討了如何通過(guò)編碼確定某用戶(hù)是否屬于某角色,以及如何相應地調整頁(yè)面上的功能。 快樂(lè )編程! |
聯(lián)系客服