以使用者為基礎的授權 (VB)
注意
自本文撰寫以來,ASP.NET 成員資格提供者已由 ASP.NET Identity 取代。 我們強烈建議更新應用程式以使用 ASP.NET Identity 平台,而不是撰寫本文時介紹的成員資格提供者。 與 ASP.NET 成員資格系統相比,ASP.NET Identity 具有許多優勢,包括:
- 更好的效能
- 改進的可擴展性和可測試性
- 支援 OAuth、OpenID Connect 和雙重認證
- 基於宣告的身分支援
- 與 ASP.Net Core 更好的互通性
在本教學課程中,我們將探討如何限制頁面的存取,以及透過各種技術限制頁面層級功能。
簡介
大部分提供使用者帳戶的 Web 應用程式會執行此動作,以限制特定訪客存取網站內的特定頁面。 例如,在大部分的在線消息板網站中,所有使用者 -- 匿名和已驗證 - 都能夠檢視消息板的文章,但只有已驗證的使用者可以瀏覽網頁來建立新的文章。 而且可能有系統管理頁面只能存取特定使用者(或一組特定使用者)。 此外,頁面層級的功能可能會依使用者而異。 檢視文章清單時,已驗證的用戶會顯示每個文章的評分介面,而匿名訪客則無法使用此介面。
ASP.NET 可讓您輕鬆地定義使用者型授權規則。 只要在 中 Web.config
加上一點標記,就可以鎖定特定網頁或整個目錄,以便只能存取指定的用戶子集。 您可以透過程序設計方式和宣告式方式,根據目前登入的用戶開啟或關閉頁面層級功能。
在本教學課程中,我們將探討如何限制頁面的存取,以及透過各種技術限制頁面層級功能。 現在就開始吧!
查看 URL 授權工作流程
如窗體驗證概觀教學課程中所述,當 ASP.NET 運行時間處理 ASP.NET 資源的要求時,要求在其生命週期中會引發一些事件。 HTTP 模組 是Managed類別,其程式代碼會執行,以回應要求生命週期中的特定事件。 ASP.NET 隨附許多 HTTP 模組,這些模組會在幕後執行基本工作。
其中一個這類 HTTP 模組是 FormsAuthenticationModule
。 如先前教學課程所述,的主要功能 FormsAuthenticationModule
是判斷目前要求的身分識別。 這可藉由檢查窗體驗證票證來完成,該票證位於Cookie或內嵌在URL中。 此識別會在事件期間AuthenticateRequest
進行。
另一個重要的 HTTP 模組是 UrlAuthorizationModule
,它會引發以回應 AuthorizeRequest
事件 (事件發生在事件之後 AuthenticateRequest
)。 會 UrlAuthorizationModule
檢查 中的 Web.config
組態標記,以判斷目前的身分識別是否有權流覽指定的頁面。 此程式稱為 URL 授權。
我們將檢查步驟 1 中 URL 授權規則的語法,但首先讓我們看看 UrlAuthorizationModule
該怎麼做,取決於要求是否獲得授權。 UrlAuthorizationModule
如果判斷要求已獲得授權,則不會執行任何動作,且要求會在其生命週期中繼續進行。 不過,如果要求 未經 授權,則會 UrlAuthorizationModule
中止生命週期,並指示 Response
對象傳回 HTTP 401 未經授權 狀態。 使用窗體驗證時,永遠不會將 HTTP 401 狀態傳回給客戶端,因為如果 FormsAuthenticationModule
偵測到 HTTP 401 狀態會將其修改為 HTTP 302 重新 導向至登入頁面。
圖 1 說明 ASP.NET 管線、 FormsAuthenticationModule
、以及 UrlAuthorizationModule
未經授權的要求送達時的工作流程。 特別是,圖 1 顯示匿名訪客的要求 ProtectedPage.aspx
,這是拒絕匿名使用者存取的頁面。 由於訪客是匿名的,因此會 UrlAuthorizationModule
中止要求並傳回 HTTP 401 未經授權狀態。 然後將 FormsAuthenticationModule
401 狀態轉換成 302 [重新導向至登入] 頁面。 透過登入頁面驗證用戶之後,會將他重新導向至 ProtectedPage.aspx
。 這次會 FormsAuthenticationModule
根據他的驗證票證來識別使用者。 現在訪客已經過驗證, UrlAuthorizationModule
允許存取頁面。
圖 1:窗體驗證和 URL 授權工作流程(按兩下以檢視完整大小的影像)
圖 1 描述匿名訪客嘗試存取匿名用戶無法使用的資源時所發生的互動。 在這種情況下,匿名訪客會重新導向至登入頁面,並嘗試瀏覽查詢字串中指定的頁面。 一旦使用者成功登入,她就會自動重新導向回她一開始嘗試檢視的資源。
當匿名使用者提出未經授權的要求時,此工作流程很簡單,而且很容易讓訪客瞭解所發生的情況和原因。 但請記住,即使已驗證的使用者提出要求,也會FormsAuthenticationModule
將任何未經授權的使用者重新導向至登入頁面。 如果已驗證的使用者嘗試流覽她缺少授權的頁面,這可能會導致令人困惑的用戶體驗。
假設您的網站已設定其 URL 授權規則,使得 ASP.NET 頁面 OnlyTito.aspx
只能存取 Tito。 現在,假設 Sam 造訪網站、登入,然後嘗試造訪 OnlyTito.aspx
。 將會 UrlAuthorizationModule
停止要求生命週期,並傳回 HTTP 401 未經授權狀態,此 FormsAuthenticationModule
狀態會偵測到 Sam,然後將 Sam 重新導向至登入頁面。 不過,由於 Sam 已經登入,她可能想知道為什麼她被送回登入頁面。 她可能會因為某種原因而失去登入認證,或者她輸入無效的認證。 如果 Sam 從登入頁面重新輸入認證,她將會登入(再次)並重新導向至 OnlyTito.aspx
。 會 UrlAuthorizationModule
偵測到 Sam 無法造訪此頁面,且她將會返回登入頁面。
圖 2 描述這個令人困惑的工作流程。
圖 2:預設工作流程可能會導致混亂的循環(按兩下以檢視完整大小的影像)
圖 2 中說明的工作流程,即使是最精明的計算機訪客,也可以快速混淆。 我們將探討如何在步驟 2 中防止這個令人困惑的迴圈。
注意
ASP.NET 使用兩種機制來判斷目前使用者是否可以存取特定網頁:URL 授權和檔案授權。 檔案授權是由 FileAuthorizationModule
實作,它會藉由諮詢要求的檔案 ACL 來判斷授權單位。 檔案授權最常與 Windows 驗證 搭配使用,因為 ACL 是適用於 Windows 帳戶的許可權。 使用窗體驗證時,不論使用者瀏覽網站,所有操作系統和文件系統層級要求都會由相同的 Windows 帳戶執行。 由於本教學課程系列著重於窗體驗證,因此我們不會討論檔案授權。
URL 授權的範圍
UrlAuthorizationModule
是屬於 ASP.NET 運行時間一部分的 Managed 程式代碼。 在 Microsoft 網際網路資訊服務 (IIS) 網頁伺服器的第 7 版之前,IIS 的 HTTP 管線與 ASP.NET 運行時間管線之間有明顯的障礙。 簡言之,在 IIS 6 和更早版本中,ASP。 UrlAuthorizationModule
只有當要求從 IIS 委派給 ASP.NET 運行時間時,NET 才會執行。 根據預設,IIS 會處理靜態內容本身,例如 HTML 頁面和 CSS、JavaScript 和圖像檔,而且只有在要求擴展名為 、 .asmx
或 .ashx
的頁面時.aspx
,才會將要求交給 ASP.NET 運行時間。
不過,IIS 7 允許整合式 IIS 和 ASP.NET 管線。 透過幾個組態設定,您可以設定 IIS 7 來針對所有要求叫UrlAuthorizationModule
用 ,這表示可以針對任何類型的檔案定義 URL 授權規則。 此外,IIS 7 也包含自己的 URL 授權引擎。 如需 ASP.NET 整合和 IIS 7 原生 URL 授權功能的詳細資訊,請參閱 瞭解 IIS7 URL 授權。 如需深入瞭解 ASP.NET 和 IIS 7 整合,請挑選 Shahram Khosravi 書籍、 專業 IIS 7 和 ASP.NET 整合式程式設計 (ISBN:978-0470152539)。
簡言之,在 IIS 7 之前的版本中,URL 授權規則只會套用至 ASP.NET 運行時間所處理的資源。 但使用 IIS 7 時,可以使用 IIS 的原生 URL 授權功能或整合 ASP。NET 會 UrlAuthorizationModule
進入 IIS 的 HTTP 管線,藉此將這項功能擴充至所有要求。
注意
ASP 的方式有一些微妙但重要的差異。NET 和 UrlAuthorizationModule
IIS 7 的 URL 授權功能會處理授權規則。 本教學課程不會檢查 IIS 7 的 URL 授權功能,或相較於 剖析授權規則 UrlAuthorizationModule
的差異。 如需這些主題的詳細資訊,請參閱 MSDN 或 www.iis.net 上的 IIS 7 檔。
步驟 1:在 中定義 URL 授權規則Web.config
會 UrlAuthorizationModule
根據應用程式組態中定義的 URL 授權規則,判斷是否要授與或拒絕特定身分識別所要求資源的存取權。 授權規則會以和 子元素的形式<allow>
在 元素中拼出<authorization>
。<deny>
每個 <allow>
與 <deny>
子元素都可以指定:
- 特定使用者
- 以逗號分隔的用戶清單
- 所有匿名使用者,以問號表示(?)
- 所有使用者,以星號表示 。
下列標記說明如何使用 URL 授權規則來允許使用者 Tito 和 Scott,並拒絕所有其他專案:
<authorization>
<allow users="Tito, Scott" />
<deny users="*" />
</authorization>
元素 <allow>
會定義允許哪些使用者 - Tito 和 Scott - 而 <deny>
元素會 指示所有使用者 遭到拒絕。
注意
<allow>
和 <deny>
元素也可以指定角色的授權規則。 我們將在未來的教學課程中檢查角色型授權。
下列設定會授與 Sam 以外的任何人的存取權(包括匿名訪客):
<authorization>
<deny users="Sam" />
</authorization>
若要只允許已驗證的使用者,請使用下列設定來拒絕存取所有匿名使用者:
<authorization>
<deny users="?" />
</authorization>
授權規則定義於 中的 Web.config
元素內<system.web>
,並套用至 Web 應用程式中的所有 ASP.NET 資源。 通常,應用程式對於不同區段有不同的授權規則。 例如,在電子商務網站上,所有訪客都可以使用產品、查看產品評論、搜尋目錄等等。 不過,只有已驗證的使用者才能連線到結帳或頁面來管理其出貨記錄。 此外,可能有部分網站只能由選取的使用者存取,例如網站管理員。
ASP.NET 可讓您輕鬆地為網站中的不同檔案和資料夾定義不同的授權規則。 根資料夾檔案 Web.config
中指定的授權規則會套用至網站中的所有 ASP.NET 資源。 不過,透過新增 Web.config
具有 <authorization>
區段的 ,即可覆寫特定資料夾的這些預設授權設定。
讓我們更新網站,讓只有已驗證的使用者才能瀏覽資料夾中的 ASP.NET 頁面 Membership
。 若要達成此目的,我們需要將檔案新增 Web.config
至資料夾, Membership
並將其授權設定設定為拒絕匿名使用者。 以滑鼠右鍵按兩下 Membership
方案總管中的資料夾,從操作選單中選擇 [新增專案] 選單,然後新增名為 Web.config
的新 Web 組態檔。
圖 3:將檔案 Web.config
新增至 Membership
資料夾 (按一下以檢視完整大小的影像)
此時,您的項目應該包含兩 Web.config
個檔案:一個在根目錄中,另一個在 Membership
資料夾中。
圖 4:您的應用程式現在應該包含兩個 Web.config
檔案(按兩下以檢視完整大小的影像)
更新資料夾中的 Membership
組態檔,使其禁止存取匿名使用者。
<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</configuration>
這樣就全部完成了!
若要測試這項變更,請瀏覽瀏覽器中的首頁,並確定您已註銷。由於 ASP.NET 應用程式的預設行為是允許所有訪客,而且因為我們未對根目錄的檔案進行任何授權修改,因此我們可以以匿名訪客身分流覽根目錄中的 Web.config
檔案。
按兩下左側資料列中找到的 [建立使用者帳戶] 連結。 這會帶您前往 ~/Membership/CreatingUserAccounts.aspx
。 Web.config
由於資料夾中的Membership
檔案會定義禁止匿名存取的授權規則,UrlAuthorizationModule
因此 會中止要求並傳回 HTTP 401 未經授權狀態。 會將此修改為 302 重新 FormsAuthenticationModule
導向狀態,並將我們傳送至登入頁面。 請注意,我們嘗試存取的頁面會CreatingUserAccounts.aspx
透過 ReturnUrl
querystring 參數傳遞至登入頁面。
圖 5:由於 URL 授權規則禁止匿名存取,因此我們會重新導向至登入頁面(按兩下以檢視完整大小的影像)
成功登入時,我們會重新導向至 CreatingUserAccounts.aspx
頁面。 這次允許 UrlAuthorizationModule
存取頁面,因為我們不再匿名。
將 URL 授權規則套用至特定位置
區 <system.web>
段中 Web.config
定義的授權設定會套用至該目錄中的所有 ASP.NET 資源及其子目錄(直到其他檔案覆寫為止 Web.config
)。 不過,在某些情況下,我們可能會希望指定目錄中的所有 ASP.NET 資源都有特定的授權設定,但一或兩個特定頁面除外。 若要達成此目的,您可以在 中Web.config
新增 元素,並將它指向授權規則不同的檔案,並在其中定義其唯一<location>
的授權規則。
為了說明如何使用 <location>
元素來覆寫特定資源的組態設定,讓我們自定義授權設定,讓只有 Tito 可以流覽 CreatingUserAccounts.aspx
。 若要達成此目的,請將 專案新增 <location>
至 Membership
資料夾的 Web.config
檔案,並更新其標記,使其看起來如下:
<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
<location path="CreatingUserAccounts.aspx">
<system.web>
<authorization>
<allow users="Tito" />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
中的 <authorization>
<system.web>
元素會定義資料夾及其子資料夾中 ASP.NET 資源 Membership
的預設 URL 授權規則。 元素 <location>
可讓我們覆寫特定資源的這些規則。 在上述標記中,元素 <location>
會參考 CreatingUserAccounts.aspx
頁面,並指定其授權規則,例如允許 Tito,但拒絕其他人。
若要測試此授權變更,請從以匿名使用者身分瀏覽網站開始。 如果您嘗試瀏覽資料夾中的任何頁面 Membership
,例如 UserBasedAuthorization.aspx
, UrlAuthorizationModule
將會拒絕要求,而且系統會將您重新導向至登入頁面。 例如,登入之後,您可以瀏覽資料夾中的任何頁面Membership
,但 除外CreatingUserAccounts.aspx
。 嘗試以任何人身分登入 CreatingUserAccounts.aspx
,但 Tito 會導致未經授權的存取嘗試,將您重新導向回登入頁面。
注意
元素 <location>
必須出現在組態的 <system.web>
元素之外。 您需要針對要覆寫其授權設定的每個資源使用不同的 <location>
元素。
查看如何使用UrlAuthorizationModule
授權規則授與或拒絕存取
會 UrlAuthorizationModule
判斷是否要一次分析 URL 授權規則,從第一個 URL 開始,並向下運作,來授權特定 URL 的特定身分識別。 只要找到相符專案,使用者就會被授與或拒絕存取權,視在 或 <deny>
專案中找到<allow>
相符專案而定。 如果找不到相符專案,則會將使用者授與存取權。 因此,如果您想要限制存取,您必須使用 元素作為URL授權組態中的最後一個專案 <deny>
。 如果您省略元素<deny>
,將會授與所有使用者的存取權。
若要進一步瞭解 用來判斷授權單位的程式 UrlAuthorizationModule
,請考慮我們稍早在此步驟中探討的範例 URL 授權規則。 第一個規則是允許 <allow>
存取 Tito 和 Scott 的專案。 第二個 <deny>
規則是拒絕存取所有人的專案。 如果匿名使用者造訪,則 UrlAuthorizationModule
一開始會詢問:匿名的 Scott 或 Tito 嗎? 顯然,答案是否,所以它會繼續進行第二個規則。 每個人都在一組匿名嗎? 因為這裡的答案是 [是],所以 <deny>
規則會生效,而訪客會重新導向至登入頁面。 同樣地,如果吉森正在訪問, UrlAuthorizationModule
首先問,吉松是斯科特還是蒂托? 既然她不是, UrlAuthorizationModule
第二個問題,吉松在每個人的集合裡嗎? 她也是,所以她也被拒絕進入。 最後,如果蒂托訪問,提出的 UrlAuthorizationModule
第一個問題是肯定的答案,所以蒂托獲得存取權。
由於會 UrlAuthorizationModule
從上而下處理授權規則,因此在任何相符專案上停止,因此在較不特定的規則之前,必須有更特定的規則。 也就是說,若要定義禁止 Jisun 和匿名使用者的授權規則,但允許所有其他已驗證的使用者,您會從影響 Jisun 的最特定規則開始,然後繼續進行較不明確的規則-這些規則允許所有其他已驗證的使用者,但拒絕所有匿名使用者。 下列 URL 授權規則會先拒絕 Jisun,然後拒絕任何匿名使用者,以實作此原則。 Jisun 以外的任何已驗證用戶都會獲得存取權,因為這些語句都不符合 <deny>
。
<authorization>
<deny users="Jisun" />
<deny users="?" />
</authorization>
步驟 2:修正未經授權的已驗證使用者的工作流程
如我們稍早在本教學課程的<查看 URL 授權工作流程>一節中所討論,每當未經授權的要求轉譯時,就會 UrlAuthorizationModule
中止要求並傳回 HTTP 401 未經授權狀態。 這個 401 狀態會由 FormsAuthenticationModule
修改為 302 重新導向狀態,以將用戶傳送至登入頁面。 即使使用者經過驗證,此工作流程也會發生在任何未經授權的要求上。
將已驗證的用戶傳回登入頁面可能會混淆,因為它們已經登入系統。 透過一些工作,我們可以藉由將進行未經授權的要求的已驗證使用者重新導向至說明他們嘗試存取受限制頁面的頁面,來改善此工作流程。
首先,在名為 UnauthorizedAccess.aspx
的 Web 應用程式根資料夾中建立新的 ASP.NET 頁面;別忘了將此頁面與 Site.master
主版頁面產生關聯。 建立此頁面之後,請移除參考 LoginContent
ContentPlaceHolder 的內容控制件,以便顯示主版頁面的預設內容。 接下來,新增說明情況的訊息,也就是用戶嘗試存取受保護的資源。 新增這類訊息之後, UnauthorizedAccess.aspx
頁面的宣告式標記看起來應該如下所示:
<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false"
CodeFile="UnauthorizedAccess.aspx.cs" Inherits="UnauthorizedAccess"
Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent"
Runat="Server">
<h2>Unauthorized Access</h2>
<p>
You have attempted to access a page that you are not authorized to view.
</p>
<p>
If you have any questions, please contact the site administrator.
</p>
</asp:Content>
我們現在需要改變工作流程,以便如果未經授權的要求是由已驗證的用戶執行,則會將他們傳送至 UnauthorizedAccess.aspx
頁面,而不是登入頁面。 將未經授權的要求重新導向至登入頁面的邏輯會埋葬在 類別的私人 FormsAuthenticationModule
方法內,因此我們無法自定義此行為。 不過,我們可以執行的動作是將自己的邏輯新增至登入頁面,以視需要將使用者重新導向至 UnauthorizedAccess.aspx
。
當 將 FormsAuthenticationModule
未經授權的訪客重新導向至登入頁面時,它會將要求的未授權 URL 附加至名稱 ReturnUrl
為 的 querystring。 例如,如果未經授權的使用者嘗試造訪 OnlyTito.aspx
,則會 FormsAuthenticationModule
將他們重新導向至 Login.aspx?ReturnUrl=OnlyTito.aspx
。 因此,如果已驗證的使用者透過包含 ReturnUrl
參數的 querystring 連線到登入頁面,則我們知道這個未經驗證的使用者剛嘗試流覽她未獲授權檢視的頁面。 在這種情況下,我們想要將她重新導向至 UnauthorizedAccess.aspx
。
若要達成此目的,請將下列程式代碼新增至登入頁面的 Page_Load
事件處理程式:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
If Request.IsAuthenticated AndAlso Not String.IsNullOrEmpty(Request.QueryString("ReturnUrl")) Then
' This is an unauthorized, authenticated request...
Response.Redirect("~/UnauthorizedAccess.aspx")
End If
End If
End Sub
上述程式代碼會將已驗證且未經授權的使用者重新導向至 UnauthorizedAccess.aspx
頁面。 若要查看此邏輯運作情形,請以匿名訪客身分瀏覽網站,然後按兩下左側數據行中的 [建立使用者帳戶] 連結。 這會帶您前往 ~/Membership/CreatingUserAccounts.aspx
頁面,在步驟 1 中,我們設定為只允許存取 Tito。 由於禁止匿名使用者,因此會將 FormsAuthenticationModule
我們重新導向回登入頁面。
此時,我們會匿名,因此 Request.IsAuthenticated
會傳 False
回 ,而且不會重新導向至 UnauthorizedAccess.aspx
。 相反地,會顯示登入頁面。 以 Tito 以外的使用者身分登入,例如 Bruce。 輸入適當的認證之後,登入頁面會將我們重新導向回 ~/Membership/CreatingUserAccounts.aspx
。 不過,由於只有 Tito 可以存取此頁面,因此我們未經授權才能檢視它,並立即返回登入頁面。 不過,這次會 Request.IsAuthenticated
傳 True
回 (和 ReturnUrl
querystring 參數存在),因此我們會重新導向至 UnauthorizedAccess.aspx
頁面。
圖 6:已驗證、未經授權的使用者會被重新導向至 UnauthorizedAccess.aspx
(按兩下以檢視完整大小的影像)
透過縮短圖 2 中所述的迴圈,此自定義工作流程會呈現更合理且直接的用戶體驗。
步驟 3:根據目前登入的使用者限制功能
URL 授權可讓您輕鬆地指定粗略的授權規則。 如我們在步驟 1 中所見,透過 URL 授權,我們可以簡潔地說明允許的身分識別,以及哪些身分識別遭到拒絕,無法檢視資料夾中的特定頁面或所有頁面。 不過,在某些情況下,我們可能想要允許所有使用者瀏覽頁面,但根據瀏覽頁面的使用者限制頁面的功能。
請考慮允許已驗證訪客檢閱其產品的電子商務網站案例。 當匿名使用者瀏覽產品的頁面時,他們只會看到產品資訊,而不會有機會離開評論。 不過,流覽相同頁面的已驗證使用者會看到檢閱介面。 如果已驗證的使用者尚未檢閱此產品,介面會讓他們提交檢閱;否則,它會向他們顯示他們先前提交的評論。 若要進一步採取此案例,產品頁面可能會顯示其他資訊,併為那些為電子商務公司工作的使用者提供擴充功能。 例如,產品頁面可能會列出庫存,並包含選項,以在員工瀏覽時編輯產品的價格和描述。
這類精細的授權規則可以以宣告方式或程序設計方式實作(或透過兩者的某些組合)。 在下一節中,我們將瞭解如何透過LoginView控件實作精細授權。 接下來,我們將探索程式設計技術。 不過,我們必須先建立功能相依於使用者瀏覽的頁面,才能查看套用精細授權規則。
讓我們建立一個頁面,列出 GridView 內特定目錄中的檔案。 除了列出每個檔案的名稱、大小和其他資訊,GridView 也會包含 LinkButtons 的兩個數據行:一個標題為 View,另一個標題為 Delete。 如果按下 [檢視 LinkButton],則會顯示所選檔案的內容;如果按下 [刪除 LinkButton],則會刪除檔案。 讓我們一開始建立此頁面,使其檢視和刪除功能可供所有使用者使用。 在 [使用 LoginView 控件和以程式設計方式限制功能] 區段中,我們將瞭解如何根據瀏覽頁面的使用者來啟用或停用這些功能。
注意
我們即將建置的 [ASP.NET] 頁面會使用 GridView 控件來顯示檔案清單。 由於本教學課程系列著重於窗體驗證、授權、用戶帳戶和角色,我不想花太多時間討論 GridView 控件的內部工作。 雖然本教學課程提供設定此頁面的特定逐步指示,但不會深入探討特定選項的原因,或特定屬性對轉譯輸出有何影響的詳細數據。 如需 GridView 控件的完整檢查,請參閱我在 ASP.NET 2.0 教學課程系列中使用數據。
從開啟 UserBasedAuthorization.aspx
資料夾中的檔案開始, Membership
並將 GridView 控件新增至名為 FilesGrid
的頁面。 從 GridView 的智慧標記中,按兩下 [編輯資料行] 連結以啟動 [欄位] 對話方塊。 從這裡取消核取左下角的 [自動產生字段] 複選框。 接下來,從左上角新增 [選取] 按鈕、[刪除] 按鈕和兩個 BoundFields(您可以在 CommandField 類型下找到 [選取和刪除] 按鈕)。 將 [選取] 按鈕的屬性 SelectText
設定為 [檢視],並將第一個 BoundField 的 HeaderText
和 DataField
屬性設定為 [名稱]。 將第二個 HeaderText
BoundField 的屬性設定為 Size in Bytes,其DataField
屬性設定為 Length,將其 屬性設定{0:N0}為 ,並將DataFormatString
屬性HtmlEncode
設定為 False。
設定 GridView 的數據行之後,按兩下 [確定] 關閉 [字段] 對話方塊。 從 屬性視窗,將 GridView 的 DataKeyNames
屬性設定為 FullName
。 此時,GridView 的宣告式標記看起來應該如下所示:
<asp:GridView ID="FilesGrid" DataKeyNames="FullName" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:CommandField SelectText="View" ShowSelectButton="True"/>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:BoundField DataField="Length" DataFormatString="{0:N0}"
HeaderText="Size in Bytes" HtmlEncode="False" />
</Columns>
</asp:GridView>
建立 GridView 的標記之後,我們即可撰寫程式代碼,以擷取特定目錄中的檔案,並將其系結至 GridView。 將下列程式代碼新增至頁面的 Page_Load
事件處理程式:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Dim appPath As String = Request.PhysicalApplicationPath
Dim dirInfo As New DirectoryInfo(appPath)
Dim files() As FileInfo = dirInfo.GetFiles()
FilesGrid.DataSource = files
FilesGrid.DataBind()
End If
End Sub
上述程式代碼會使用 類別DirectoryInfo
來取得應用程式根資料夾中的檔案清單。 方法會將GetFiles()
目錄中的所有檔案當做 對象數位FileInfo
傳回,然後系結至 GridView。 物件 FileInfo
具有各種屬性,例如 Name
、 Length
和 IsReadOnly
等。 如您在宣告式標記中所見,GridView 只會 Name
顯示 和 Length
屬性。
注意
DirectoryInfo
和 FileInfo
類別位於 命名空間中System.IO
。 因此,您必須在這些類別名稱前面加上其命名空間名稱,或將命名空間匯入類別檔案中(透過 Imports System.IO
)。
請花點時間透過瀏覽器瀏覽此頁面。 它會顯示位於應用程式根目錄中的檔案清單。 按兩下任何 View 或刪除 LinkButtons 會導致回傳,但不會發生任何動作,因為我們尚未建立必要的事件處理程式。
圖 7:GridView 列出 Web 應用程式根目錄中的檔案 (按兩下以檢視完整大小的影像)
我們需要一個方法來顯示所選取檔案的內容。 返回 Visual Studio,並在 GridView 上方新增名為 FileContents
的 TextBox。 將其 TextMode
屬性設定為 MultiLine
,並將其 和 Rows
Columns
屬性分別設定為95%和10%。
<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>
接下來,建立 GridView SelectedIndexChanged
事件的 事件處理程式,並新增下列程式代碼:
Protected Sub FilesGrid_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles FilesGrid.SelectedIndexChanged
' Open the file and display it
Dim fullFileName As String = FilesGrid.SelectedValue.ToString()
Dim contents As String = File.ReadAllText(fullFileName)
FileContents.Text = contents
End Sub
此程式代碼會使用 GridView 的 SelectedValue
屬性來判斷所選取檔案的完整檔名。 在內部, DataKeys
會參考集合以取得 SelectedValue
,因此您必須將 GridView 的 DataKeyNames
屬性設定為 Name,如此步驟稍早所述。 類別File
可用來將選取的檔案內容讀入字串,然後指派給 FileContents
TextBox Text
的屬性,從而在頁面上顯示所選檔案的內容。
圖 8:選取的檔案內容會顯示在 TextBox 中(按兩下以檢視完整大小的影像)
注意
如果您檢視包含 HTML 標記的檔案內容,然後嘗試檢視或刪除檔案,您會收到 HttpRequestValidationException
錯誤。 這是因為在回傳時,TextBox 的內容會傳回至網頁伺服器。 根據預設,ASP.NET 每當偵測到潛在的危險回傳內容,例如 HTML 標記時,就會引發 HttpRequestValidationException
錯誤。 若要停用此錯誤發生,請新增 ValidateRequest="false"
至 @Page
指示詞來關閉頁面的要求驗證。 如需要求驗證的優點,以及停用要求時應採取哪些預防措施的詳細資訊,請參閱 要求驗證 - 防止腳本攻擊。
最後,為 GridView 的事件新增具有下列程式代碼的RowDeleting
事件處理程式:
Protected Sub FilesGrid_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs)Handles FilesGrid.RowDeleting
Dim fullFileName As String = FilesGrid.DataKeys(e.RowIndex).Value.ToString()
FileContents.Text = String.Format("You have opted to delete {0}.", fullFileName)
' To actually delete the file, uncomment the following line
' File.Delete(fullFileName)
End Sub
程序代碼只會在 TextBox 中FileContents
顯示要刪除之檔案的完整名稱,而不會實際刪除檔案。
圖 9:按兩下 [刪除] 按鈕不會實際刪除檔案 (按兩下以檢視完整大小的影像)
在步驟 1 中,我們設定了 URL 授權規則,禁止匿名用戶檢視資料夾中的頁面 Membership
。 為了更好地展示精細的驗證,讓我們允許匿名用戶瀏覽 UserBasedAuthorization.aspx
頁面,但功能有限。 若要開啟此頁面以讓所有使用者存取,請將下列 <location>
元素新增至 Web.config
資料夾中的 Membership
檔案:
<location path="UserBasedAuthorization.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
新增此 <location>
元素之後,請註銷網站來測試新的 URL 授權規則。 身為匿名使用者,您應該允許瀏覽 UserBasedAuthorization.aspx
頁面。
目前,任何已驗證或匿名的使用者都可以瀏覽 UserBasedAuthorization.aspx
頁面並檢視或刪除檔案。 讓我們讓它只讓已驗證的用戶能夠檢視檔案的內容,而且只有 Tito 可以刪除檔案。 這類精細授權規則可以宣告方式、以程序設計方式或透過這兩種方法的組合來套用。 讓我們使用宣告式方法來限制誰可以檢視檔案的內容;我們將使用程式設計方法來限制誰可以刪除檔案。
使用 LoginView 控件
如我們過去教學課程中所見,LoginView 控件對於顯示已驗證和匿名使用者的不同介面很有用,並提供一種簡單的方法來隱藏匿名用戶無法存取的功能。 由於匿名使用者無法檢視或刪除檔案,因此只有在已驗證的使用者瀏覽頁面時,才需要顯示 FileContents
TextBox。 若要達成此目的,請將 LoginView 控件新增至頁面、將它命名為 LoginViewForFileContentsTextBox
,然後將 TextBox 的宣告式標記移至 FileContents
LoginView 控制件的 LoggedInTemplate
。
<asp:LoginView ID=" LoginViewForFileContentsTextBox " runat="server">
<LoggedInTemplate>
<p>
<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>
</p>
</LoggedInTemplate>
</asp:LoginView>
LoginView 範本中的 Web 控制項無法再直接從程式代碼後置類別存取。 例如, FilesGrid
GridView 的 SelectedIndexChanged
和 RowDeleting
事件處理程式目前會參考 FileContents
TextBox 控件,其程式代碼如下:
FileContents.Text = text
不過,此程式代碼已不再有效。 無法直接存取將 FileContents
TextBox 移至 LoggedInTemplate
TextBox。 相反地,我們必須使用 FindControl("controlId")
方法來以程序設計方式參考 控件。 FilesGrid
更新事件處理程式以參考 TextBox,如下所示:
Dim FileContentsTextBox As TextBox = CType(LoginViewForFileContentsTextBox.FindControl("FileContents"),TextBox)
FileContentsTextBox.Text = text
將 TextBox 移至 LoginView 的 LoggedInTemplate
,並更新頁面的程式代碼以使用 FindControl("controlId")
模式參考 TextBox 之後,請以匿名使用者身分瀏覽頁面。 如圖 10 所示, FileContents
不會顯示 TextBox。 不過,仍會顯示 View LinkButton。
圖 10:LoginView 控件只會轉譯 FileContents
已驗證使用者的 TextBox(按兩下以檢視完整大小的影像)
隱藏匿名使用者的 [檢視] 按鈕的其中一種方式是將 GridView 字段轉換成 TemplateField。 這會產生範本,其中包含 View LinkButton 的宣告式標記。 然後,我們可以將 LoginView 控件新增至 TemplateField,並將 LinkButton 放在 LoginView 的 LoggedInTemplate
中,藉此隱藏匿名訪客的 [檢視] 按鈕。 若要達成此目的,請按兩下 GridView 智慧標記中的 [編輯資料行] 連結,以啟動 [字段] 對話方塊。 接下來,從左下角的清單中選取 [選取] 按鈕,然後按下 [將此欄位轉換成 TemplateField] 連結。 這樣做會從下列專案修改欄位的宣告式標記:
<asp:CommandField SelectText="View" ShowSelectButton="True"/>
變更為:
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
CommandName="Select" Text="View"></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
此時,我們可以將 LoginView 新增至 TemplateField。 下列標記只會針對已驗證的用戶顯示 View LinkButton。
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LoginView ID="LoginView1" runat="server">
<LoggedInTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
CommandName="Select" Text="View"></asp:LinkButton>
</LoggedInTemplate>
</asp:LoginView>
</ItemTemplate>
</asp:TemplateField>
如圖 11 所示,即使隱藏數據行內的 View LinkButtons,最終結果並不如 [檢視] 資料行所顯示。 我們將探討如何在下一節中隱藏整個 GridView 數據行(而不只是 LinkButton)。
圖 11:LoginView 控制件隱藏匿名訪客的檢視連結按鈕 (按一下以檢視完整大小的影像)
以程序設計方式限制功能
在某些情況下,宣告式技術不足以將功能限制為頁面。 例如,特定頁面功能的可用性可能取決於瀏覽頁面的使用者是匿名或已驗證的準則。 在這種情況下,可以透過程序設計方式顯示或隱藏各種使用者介面元素。
為了以程序設計方式限制功能,我們需要執行兩項工作:
- 判斷瀏覽頁面的使用者是否可以存取功能,以及
- 根據使用者是否可以存取有問題的功能,以程式設計方式修改使用者介面。
為了示範這兩項工作的應用,我們只允許 Tito 從 GridView 刪除檔案。 然後,我們的第一項工作是判斷它是否為 Tito 瀏覽頁面。 一旦確定之後,我們需要隱藏 [或顯示] GridView 的 Delete 數據行。 GridView 的數據行可透過其 Columns
屬性存取;只有當數據 Visible
行的 屬性設定為 True
(預設值) 時,才會轉譯數據行。
將下列程式代碼新增至 Page_Load
事件處理程式,再將數據系結至 GridView:
' Is this Tito visiting the page?
Dim userName As String = User.Identity.Name
If String.Compare(userName, "Tito", True) = 0 Then
' This is Tito, SHOW the Delete column
FilesGrid.Columns(1).Visible = True
Else
' This is NOT Tito, HIDE the Delete column
FilesGrid.Columns(1).Visible = False
End If
如我們在窗體驗證概觀教學課程中所討論,User.Identity.Name
會傳回身分識別的名稱。 這會對應至登入控件中輸入的用戶名稱。 如果是 Tito 瀏覽頁面,GridView 的第二個 Visible
資料行屬性會設定為 True
,否則會設定為 False
。 淨結果是,當 Tito 以外的人員造訪頁面時,可能是另一個已驗證的使用者或匿名使用者,則不會轉譯 Delete 數據行(請參閱圖 12):不過,當 Tito 瀏覽頁面時,[刪除] 資料行就會存在(請參閱圖 13)。
圖 12:當 Tito 以外的人員流覽時,不會轉譯刪除資料行(例如 Bruce) (按兩下以檢視完整大小的影像)
圖 13:針對 Tito 轉譯刪除資料行 (按兩下以檢視完整大小的影像)
步驟 4:將授權規則套用至類別和方法
在步驟 3 中,我們不允許匿名用戶檢視檔案的內容,並禁止所有使用者,但 Tito 無法刪除檔案。 這是透過宣告式和程式設計技術,為未經授權的訪客隱藏相關聯的使用者介面元素來完成的。 在我們的簡單範例中,正確隱藏使用者介面元素很簡單,但更複雜的網站可能會有許多不同的方式來執行相同的功能呢? 在將該功能限制為未經授權的使用者時,如果我們忘記隱藏或停用所有適用的使用者介面元素,會發生什麼事?
確保未經授權的用戶無法存取特定功能片段的簡單方式,就是使用 PrincipalPermission
屬性來裝飾該類別或方法。 當 .NET 運行時間使用類別或執行其中一個方法時,它會檢查以確定目前的安全性內容具有使用 類別或執行方法的許可權。 屬性 PrincipalPermission
提供一種機制,我們可以透過此機制來定義這些規則。
讓我們示範 PrincipalPermission
如何使用 GridView SelectedIndexChanged
和 RowDeleting
事件處理程式上的 屬性,分別禁止 Tito 以外的匿名使用者和使用者執行。 我們只需要在每個函式定義上新增適當的屬性:
<PrincipalPermission(SecurityAction.Demand, Authenticated:=True)> _
Protected Sub FilesGrid_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles FilesGrid.SelectedIndexChanged
...
End Sub
<PrincipalPermission(SecurityAction.Demand, Name:="Tito")> _
Protected Sub FilesGrid_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles FilesGrid.RowDeleting
...
End Sub
事件處理程式的屬性 SelectedIndexChanged
會指示只有已驗證的使用者才能執行事件處理程式,其中當事件處理程式上的 RowDeleting
屬性將執行限制為 Tito 時。
注意
屬性可以套用至類別、方法、屬性或事件。 新增屬性時,它必須是類別、方法、屬性或事件宣告語句的一部分。 由於 Visual Basic 使用換行符做為語句分隔符,因此屬性必須出現在與宣告相同的行上,或是以換行接續字元(底線)直接出現在其上方。 在上述代碼段中,行接續字元用來將 屬性放在一行上,並將方法宣告放在另一行。
如果 Tito 以外的使用者嘗試執行 RowDeleting
事件處理程式,或未驗證的使用者嘗試執行 SelectedIndexChanged
事件處理程式,.NET 執行時間將會引發 SecurityException
。
圖 14:如果安全性內容未獲授權執行方法, SecurityException
則會擲回 (單擊以檢視完整大小的影像)
注意
若要允許多個安全性內容存取類別或方法,請使用每個安全性內容的屬性來裝飾類別或方法 PrincipalPermission
。 也就是說,若要允許 Tito 和 Bruce 執行RowDeleting
事件處理程式,請新增兩個PrincipalPermission
屬性:
<PrincipalPermission(SecurityAction.Demand, Name:="Tito")> _
<PrincipalPermission(SecurityAction.Demand, Name:="Bruce")> _
除了 ASP.NET 頁之外,許多應用程式也有包含各種層級的架構,例如商業規則和數據存取層。 這些層通常會實作為類別庫,並提供執行商業規則和數據相關功能的類別和方法。 屬性 PrincipalPermission
適用於將授權規則套用至這些層。
如需使用 PrincipalPermission
屬性定義類別和方法授權規則的詳細資訊,請參閱 Scott Guthrie 的部落格文章 :使用 PrincipalPermissionAttributes
將授權規則新增至商務和數據層。
摘要
在本教學課程中,我們已探討如何套用使用者型授權規則。 我們從 ASP 開始著手。NET 的 URL 授權架構。 在每個要求上,ASP.NET 引擎會 UrlAuthorizationModule
檢查應用程式組態中定義的URL授權規則,以判斷身分識別是否獲得存取要求資源的授權。 簡言之,URL 授權可讓您輕鬆地指定特定頁面的授權規則,或針對特定目錄中的所有頁面指定授權規則。
URL 授權架構會逐頁套用授權規則。 使用 URL 授權時,要求身分識別會獲得存取特定資源的授權。 不過,許多案例都會要求更精細的授權規則。 我們可能需要讓每個人都存取頁面,而不是定義允許誰存取頁面,而是根據瀏覽頁面的使用者,顯示不同的數據或提供不同的功能。 頁面層級授權通常牽涉到隱藏特定的使用者介面元素,以防止未經授權的使用者存取禁止的功能。 此外,您可以使用屬性來限制對類別的存取,以及針對特定使用者執行其方法。
快樂程式!
深入閱讀
有關本教學課程中討論的主題的更多資訊,請參閱以下資源:
- 使用 將授權規則新增至商務和數據層
PrincipalPermissionAttributes
- ASP.NET 授權
- IIS6 與 IIS7 安全性之間的變更
- 設定特定檔案和子目錄
- 根據使用者限制數據修改功能
- LoginView 控件快速入門
- 瞭解 IIS7 URL 授權
UrlAuthorizationModule
技術檔- 在 ASP.NET 2.0 中使用數據
關於作者
斯科特·米切爾,多個 ASP/ASP.NET 書籍的作者,4GuysFromRolla.com 的創始人,自1998年以來一直與Microsoft Web 技術合作。 Scott 擔任獨立顧問、講師和作家。 他的新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 斯科特可以透過 mitchell@4guysfromrolla.com 他在的部落格聯繫到或通過他的博客 http://ScottOnWriting.NET。
特別感謝
本教學課程系列已經過許多熱心的檢閱者檢閱。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果是這樣,請留言給我 mitchell@4GuysFromRolla.com。