以使用者為主的授權 (C#)

作者:Scott Mitchell

注意

本文撰寫之後,ASP.NET 成員資格提供者已被 ASP.NET 身分識別取代。 強烈建議您將應用程式更新為使用 ASP.NET 身分識別 平臺,而不是本文撰寫時精選的成員資格提供者。 ASP.NET 身分識別的一些優點優於 ASP.NET 成員資格系統,包括 :

  • 更好的效能
  • 改善擴充性和可測試性
  • 支援 OAuth、OpenID Connect 和雙因素驗證
  • 宣告型身分識別支援
  • 與 ASP.Net Core 的較佳互通性

下載程式代碼下載 PDF

在本教學課程中,我們將探討透過各種技術限制頁面層級功能的存取權和限制頁面層級功能。

簡介

大部分提供使用者帳戶的 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 管線、 FormsAuthenticationModuleUrlAuthorizationModule 未經授權的要求送達時的工作流程。 特別是,圖 1 顯示匿名訪客的要求 ProtectedPage.aspx ,這是拒絕匿名使用者存取的頁面。 由於訪客是匿名的,因此會 UrlAuthorizationModule 中止要求,並傳回 HTTP 401 未經授權狀態。 FormsAuthenticationModule然後將 401 狀態轉換成 302 重新導向至登入頁面。 透過登入頁面驗證使用者之後,系統會將他重新導向至 ProtectedPage.aspx 。 這次 ,會 FormsAuthenticationModule 根據使用者的驗證票證來識別使用者。 現在已驗證訪客,允許 UrlAuthorizationModule 存取頁面。

表單驗證和 URL 授權工作流程

圖 1:表單驗證和 URL 授權工作流程 (按一下以檢視完整大小的映射)

圖 1 描述匿名訪客嘗試存取匿名使用者無法使用的資源時所發生的互動。 在這種情況下,匿名訪客會重新導向至登入頁面,並嘗試造訪查詢字串中指定的頁面。 使用者成功登入之後,系統會自動將她重新導向回最初嘗試檢視的資源。

當匿名使用者提出未經授權的要求時,此工作流程很簡單,而且很容易讓訪客瞭解發生的情況和原因。 但請記住, FormsAuthenticationModule 即使已驗證的使用者提出要求,仍會將 任何 未經授權的使用者重新導向至登入頁面。 如果已驗證的使用者嘗試造訪其缺少授權的頁面,這可能會導致令人困惑的使用者體驗。

假設我們的網站已設定其 URL 授權規則,讓 ASP.NET 網頁 OnlyTito.aspx 只能存取 Tito。 現在,假設 Sam 造訪網站、登入,然後嘗試造訪 OnlyTito.aspxUrlAuthorizationModule將會停止要求生命週期,並傳回 HTTP 401 未經授權狀態, FormsAuthenticationModule 這會偵測並重新導向 Sam 至登入頁面。 不過,由於 Sam 已經登入,她可能會想知道為什麼她已送回登入頁面。 她可能會因為登入認證而遺失,或她輸入了不正確認證。 如果 Sam 從登入頁面重新輸入認證,她將會再次登入 (,) 並重新導向至 OnlyTito.aspxUrlAuthorizationModule會偵測到 Sam 無法流覽此頁面,而且她會返回登入頁面。

圖 2 描述這令人困惑的工作流程。

預設工作流程可能會導致混淆的迴圈

圖 2:預設工作流程可能會導致混淆的迴圈 (按一下以檢視大小完整的影像)

圖 2 中說明的工作流程,即使最聰明的電腦訪客,也可以快速運作。 我們將探討在步驟 2 中防止這種混淆迴圈的方式。

注意

ASP.NET 使用兩種機制來判斷目前使用者是否可以存取特定的網頁:URL 授權和檔案授權。 檔案授權是由 實作, FileAuthorizationModule 它會藉由諮詢要求的檔案 (s) ACL 來判斷授權單位。 檔案授權最常與Windows 驗證搭配使用,因為 ACL 是適用于 Windows 帳戶的許可權。 使用表單驗證時,不論流覽網站的使用者為何,所有作業系統和檔案系統層級要求都會由相同的 Windows 帳戶執行。 由於本教學課程系列著重于表單驗證,因此我們不會討論檔案授權。

URL 授權的範圍

UrlAuthorizationModule是屬於 ASP.NET 執行時間一部分的 Managed 程式碼。 在 Microsoft 的 Internet Information Services 版本 7 (IIS) Web 服務器之前,IIS 的 HTTP 管線與 ASP.NET 執行時間管線之間有不同的屏障。 簡單來說,在 IIS 6 和更早版本中,ASP。 UrlAuthorizationModule 只有在將要求從 IIS 委派給 ASP.NET 執行時間時,NET 才會執行。 根據預設,IIS 會處理靜態內容本身,例如 HTML 頁面和 CSS、JavaScript 和影像檔,而且只有在要求副檔名為 、 或 .ashx 的頁面 .aspx.asmx 時,才會對 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 授權規則,判斷是否要授與或拒絕特定身分識別所要求資源的存取權。 授權規則會以 和 <deny> 子項目的形式 <allow>元素中 <authorization>拼字。 每個 <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>

授權規則定義于 中的 <system.web>Web.config 元素內,並套用至 Web 應用程式中的所有 ASP.NET 資源。 通常,應用程式會針對不同的區段有不同的授權規則。 例如,在電子商務網站上,所有訪客都可以依序使用產品、查看產品評論、搜尋目錄等等。 不過,只有已驗證的使用者才能到達簽出或頁面來管理一個出貨歷程記錄。 此外,網站可能有部分只能由選取的使用者存取,例如網站管理員。

ASP.NET 可讓您輕鬆地為網站中的不同檔案和資料夾定義不同的授權規則。 根資料夾檔案 Web.config 中指定的授權規則會套用至網站中的所有 ASP.NET 資源。 不過,您可以使用 區段新增 來 Web.config<authorization> 覆寫特定資料夾的這些預設授權設定。

讓我們更新網站,讓只有經過驗證的使用者可以流覽資料夾中的 ASP.NET 網頁 Membership 。 若要達成此目的,我們需要將檔案新增 Web.configMembership 資料夾,並將其授權設定設定為拒絕匿名使用者。 以滑鼠右鍵按一下 Membership 方案總管中的資料夾,從操作功能表中選擇 [新增專案] 功能表,然後新增名為 Web.config 的新 Web 組態檔。

將Web.config檔案新增至 Membership 資料夾

圖 3:將檔案新增 Web.configMembership 資料夾 (按一下即可檢視完整大小的影像)

此時,您的專案應該包含兩 Web.config 個檔案:一個位於根目錄中,另一個位於 Membership 資料夾中。

您的應用程式現在應該包含兩個Web.config檔案

圖 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.configMembership 檔案會定義禁止匿名存取的授權規則,因此 會 UrlAuthorizationModule 中止要求並傳回 HTTP 401 未經授權狀態。 會將 FormsAuthenticationModule 此修改為 302 重新導向狀態,並將我們傳送至登入頁面。 請注意,我們嘗試存取 () CreatingUserAccounts.aspx 的頁面會透過 ReturnUrl querystring 參數傳遞至登入頁面。

由於 URL 授權規則禁止匿名存取,因此我們會重新導向至登入頁面

圖 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 拒絕要求,而且系統會將您重新導向至登入頁面。 登入後,例如 Scott,您可以流覽資料夾中的任何頁面 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> 規則會生效,而訪客會重新導向至登入頁面。 同樣地,如果 Jisun 正在造訪,則 UrlAuthorizationModule 一開始會詢問 Jisun 是否為 Scott 或 Tito? 由於她不是,所以 UrlAuthorizationModule 會繼續進行第二個問題:Jisun 是否在每個人的集合中? 她也拒絕存取。 最後,如果 Tito 造訪,則 所提供的 UrlAuthorizationModule 第一個問題是肯定答案,因此 Tito 會被授與存取權。

由於 會 UrlAuthorizationModule 從上而下處理授權規則,因此在任何相符專案停止時,請務必在較不特定的規則之前出現更特定的規則。 也就是說,若要定義禁止 Jisun 和匿名使用者的授權規則,但允許所有其他已驗證的使用者,您會從影響 Jisun 的最特定規則開始,然後繼續進行較不特定的規則 ,這些規則允許所有其他已驗證的使用者,但拒絕所有匿名使用者。 下列 URL 授權規則會先拒絕 Jisun,然後拒絕任何匿名使用者,以實作此原則。 Jisun 以外的任何已驗證使用者都會被授與存取權,因為這 <deny> 兩個語句都不會相符。

<authorization>
 <deny users="Jisun" />
 <deny users="?" />
</authorization>

步驟 2:修正未經授權、已驗證使用者的工作流程

As we discussed earlier in this tutorial in the A Look at the URL Authorization Workflow section, anytime an unauthorized request transpires, the UrlAuthorizationModule aborts the request and returns an HTTP 401 Unauthorized status. 這個 401 狀態會由 FormsAuthenticationModule 修改為 302 重新導向狀態,將使用者傳送至登入頁面。 即使使用者經過驗證,此工作流程也會在任何未經授權的要求上發生。

將已驗證的使用者傳回登入頁面可能會混淆,因為它們已經登入系統。 有了一些工作,我們可以藉由將發出未經授權的要求的已驗證使用者重新導向至說明他們嘗試存取受限制頁面的頁面,以改善此工作流程。

首先,在名為 UnauthorizedAccess.aspx 的 Web 應用程式根資料夾中建立新的 ASP.NET 網頁;別忘了建立此頁面與主版頁面的 Site.master 關聯。 建立此頁面之後,請移除參考 LoginContent ContentPlaceHolder 的 Content 控制項,以便顯示主版頁面的預設內容。 接下來,新增說明情況的訊息,也就是使用者嘗試存取受保護的資源。 新增這類訊息之後, UnauthorizedAccess.aspx 頁面的宣告式標記看起來應該如下所示:

<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
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 void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
        // This is an unauthorized, authenticated request...
        Response.Redirect("~/UnauthorizedAccess.aspx");
    }
}

上述程式碼會將已驗證、未經授權的使用者重新導向至 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 頁面。

已驗證,未經授權的使用者會重新導向至 UnauthorizedAccess.aspx

圖 6:已驗證、未經授權的使用者會被重新導向至 UnauthorizedAccess.aspx (按一下即可檢視完整大小的映射)

此自訂工作流程藉由縮短圖 2 中所述的週期,提供更合理且直接的使用者體驗。

步驟 3:根據目前登入的使用者限制功能

URL 授權可讓您輕鬆地指定粗略的授權規則。 如步驟 1 中所見,透過 URL 授權,我們可以簡潔地說明允許的身分識別,以及哪些身分識別遭到拒絕,而無法檢視資料夾中的特定頁面或所有頁面。 不過,在某些案例中,我們可能會允許所有使用者流覽頁面,但會根據使用者流覽頁面來限制頁面的功能。

請考慮允許經驗證訪客檢閱其產品的電子商務網站案例。 當匿名使用者流覽產品的頁面時,他們只會看到產品資訊,而不會有機會離開評論。 不過,流覽相同頁面的已驗證使用者會看到檢閱介面。 如果已驗證的使用者尚未檢閱此產品,介面會讓他們提交檢閱;否則會顯示其先前提交的評論。 若要進一步採取此案例,產品頁面可能會顯示其他資訊,並為適用于電子商務公司的使用者提供擴充功能。 例如,產品頁面可能會列出庫存,並包含選項,以在員工造訪時編輯產品的價格和描述。

這類精細的授權規則可以透過宣告方式或以程式設計方式 (或透過兩個) 的一些組合來實作。 在下一節中,我們將瞭解如何透過 LoginView 控制項實作細微授權。 接下來,我們將探索程式設計技術。 不過,在查看套用細微授權規則之前,我們必須先建立其功能相依于流覽的頁面。

讓我們建立一個頁面,列出 GridView 內特定目錄中的檔案。 除了列出每個檔案的名稱、大小和其他資訊之外,GridView 也會包含兩個 LinkButtons 資料行:一個標題為 View,另一個標題為 Delete。 如果按一下 [檢視連結][按鈕],則會顯示所選檔案的內容;如果按一下 [刪除 LinkButton],將會刪除檔案。 讓我們一開始建立此頁面,讓所有使用者都能使用其檢視和刪除功能。 在 [使用 LoginView 控制項] 和 [以程式設計方式限制功能] 區段中,我們將瞭解如何根據流覽頁面的使用者啟用或停用這些功能。

注意

我們即將建置的 [ASP.NET] 頁面會使用 GridView 控制項來顯示檔案清單。 由於本教學課程系列著重于表單驗證、授權、使用者帳戶和角色,我不想花太多時間討論 GridView 控制項的內部工作。 雖然本教學課程提供設定此頁面的特定逐步指示,但不會深入探討為何做出特定選擇的詳細資料,或轉譯輸出上特定屬性有何影響。 如需 GridView 控制項的完整檢查,請參閱在 ASP.NET 2.0 教學課程系列中使用資料。

首先,開啟 資料夾中的 UserBasedAuthorization.aspxMembership 檔案,並將 GridView 控制項新增至名為 FilesGrid 的頁面。 從 GridView 的智慧標籤中,按一下 [編輯資料行] 連結以啟動 [欄位] 對話方塊。 從這裡取消核取左下角的 [自動產生欄位] 核取方塊。 接下來,新增 [選取] 按鈕、[刪除] 按鈕,以及左上角的兩個 BoundField, ([選取] 和 [刪除] 按鈕可在 CommandField 類型) 下找到。 將 [選取] 按鈕的屬性 SelectText 設定為 [檢視],並將第一個 BoundField 的 HeaderTextDataField 屬性設定為 [名稱]。 將第二個 BoundField 的 HeaderText 屬性設定為 Size in Bytes,將其屬性設定為 Length,將其 DataField 屬性設定為 {0:N0}HtmlEncode ,並將其 DataFormatString 屬性設定為 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 void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        string appPath = Request.PhysicalApplicationPath;
        DirectoryInfo dirInfo = new DirectoryInfo(appPath);

        FileInfo[] files = dirInfo.GetFiles();

        FilesGrid.DataSource = files;
        FilesGrid.DataBind();
    }
}

上述程式碼會DirectoryInfo 使用 類別來取得應用程式根資料夾中的檔案清單。 方法會將GetFiles()目錄中的所有檔案當做 物件的陣列FileInfo傳回,然後系結至 GridView。 物件 FileInfo 具有各種屬性,例如 NameLengthIsReadOnly 等等。 如您在宣告式標記中所見,GridView 只會 Name 顯示 和 Length 屬性。

注意

DirectoryInfoFileInfo 類別位於命名空間中 System.IO。 因此,您必須在其命名空間名稱前面加上這些類別名稱,或透過) 將命名空間匯入類別檔案 (using System.IO

請花點時間流覽此頁面, 它會顯示位於應用程式根目錄中的檔案清單。 按一下任何 View 或 Delete LinkButtons 會導致回傳,但不會發生任何動作,因為我們尚未建立必要的事件處理常式。

GridView 列出 Web 應用程式根目錄中的檔案

圖 7:GridView 會列出 Web 應用程式根目錄中的檔案, (按一下即可檢視完整大小的影像)

我們需要一個方法來顯示所選取檔案的內容。 返回 Visual Studio,並在 GridView 上方新增名為 FileContents 的 TextBox。 將其 TextMode 屬性分別設定為 MultiLine ,並將其 和 ColumnsRows 屬性分別設定為 95% 和 10。

<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>

接下來,建立 GridView SelectedIndexChanged 事件的事件處理常式,並新增下列程式碼:

protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
    // Open the file and display it
    string fullFileName = FilesGrid.SelectedValue.ToString();
    string contents = File.ReadAllText(fullFileName);
    FileContents.Text = contents;
}

此程式碼會使用 GridView 的 SelectedValue 屬性來判斷所選取檔案的完整檔案名。 在內部, DataKeys 系統會參考集合以取得 SelectedValue ,因此您必須將 GridView 的 DataKeyNames 屬性設定為 Name,如此步驟稍早所述。 類別File 可用來將選取的檔案內容讀入字串,然後指派給 FileContents TextBox 的 Text 屬性,進而在頁面上顯示所選檔案的內容。

選取的檔案內容會顯示在 TextBox 中

圖 8:選取的檔案內容會顯示在 TextBox 中, (按一下即可檢視完整大小的影像)

注意

如果您檢視包含 HTML 標籤的檔案內容,然後嘗試檢視或刪除檔案,您會收到 HttpRequestValidationException 錯誤。 這是因為在回傳時,TextBox 的內容會傳回網頁伺服器。 根據預設,ASP.NET 會在偵測到潛在危險回傳內容,例如 HTML 標籤時引發 HttpRequestValidationException 錯誤。 若要停用此錯誤,請藉由將 新增 ValidateRequest="false"@Page 指示詞來關閉頁面的要求驗證。 如需要求驗證的優點,以及停用要求時應採取哪些預防措施的詳細資訊,請參閱 要求驗證 - 防止腳本攻擊

最後,為 GridView 的事件新增具有下列程式碼的RowDeleting事件處理常式:

protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    string fullFileName = 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);
}

程式碼只會顯示 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 的 SelectedIndexChangedRowDeleting 事件處理常式目前參考 FileContents TextBox 控制項的程式碼如下:

FileContents.Text = text;

不過,此程式碼已不再有效。 將 TextBox 移至 FileContents TextBox LoggedInTemplate 無法直接存取。 相反地,我們必須使用 FindControl("controlId") 方法來以程式設計方式參考 控制項。 FilesGrid更新事件處理常式以參考 TextBox,如下所示:

TextBox FileContentsTextBox = LoginViewForFileContentsTextBox.FindControl("FileContents") as TextBox;
FileContentsTextBox.Text = text;

將 TextBox 移至 LoginView 的 LoggedInTemplate ,並更新頁面的程式碼以使用 FindControl("controlId") 模式參考 TextBox 之後,請以匿名使用者身分流覽頁面。 如圖 10 所示, FileContents 不會顯示 TextBox。 不過,仍會顯示 View LinkButton。

LoginView 控制項只會轉譯已驗證使用者的 FileContents TextBox

圖 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 LinkButton,最終結果也不會像檢視資料行一樣顯示。 我們將探討如何隱藏整個 GridView 資料行 (,而不只是下一節中的 LinkButton) 。

LoginView 控制項會隱藏匿名訪客的檢視連結按鈕

圖 11:LoginView 控制項會隱藏匿名訪客的檢視連結按鈕, (按一下即可檢視大小完整的影像)

以程式設計方式限制功能

在某些情況下,宣告式技術不足以限制頁面的功能。 例如,某些頁面功能的可用性可能取決於流覽頁面的使用者是匿名還是已驗證的準則。 在這種情況下,可以透過程式設計方式顯示或隱藏各種使用者介面元素。

為了以程式設計方式限制功能,我們需要執行兩項工作:

  1. 判斷流覽頁面的使用者是否可以存取功能,以及
  2. 根據使用者是否可以存取有問題的功能,以程式設計方式修改使用者介面。

為了示範這兩項工作的應用程式,我們只允許 Tito 從 GridView 刪除檔案。 然後,我們的第一個工作是判斷它是否為流覽頁面的 Tito。 一旦決定之後,我們需要隱藏 (或顯示 GridView 的 Delete 資料行) 。 GridView 的資料行可透過其 Columns 屬性存取;只有當資料 Visible 行的 屬性設定 true 為 (預設) 時,才會轉譯資料行。

將下列程式碼新增至 Page_Load 事件處理常式,再將資料系結至 GridView:

// Is this Tito visiting the page?
string userName = User.Identity.Name;
if (string.Compare(userName, "Tito", true) == 0)
    // 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;

如我們在 表單驗證概 觀教學課程中所討論, User.Identity.Name 傳回身分識別的名稱。 這會對應至登入控制項中輸入的使用者名稱。 如果是流覽頁面的 Tito,GridView 的第二 Visible 個數據行屬性會設定 true 為 ,否則會設定為 false 。 最終結果是,當 Tito 以外的人造訪頁面時,另一個已驗證的使用者或匿名使用者,[刪除] 資料行不會轉譯 (請參閱圖 12) ;不過,當 Tito 流覽頁面時,[刪除] 資料行會 (請參閱圖 13) 。

當 Tito 以外的人造訪時,[刪除資料行] 不會轉譯 (,例如暴力密碼)

圖 12:刪除資料行在 Tito 以外的人流覽時不會轉譯,例如暴力密碼 () (按一下即可檢視大小完整的影像)

[刪除資料行] 會針對 Tito 轉譯

圖 13:[刪除資料行] 會針對 Tito 轉譯 (按一下以檢視大小完整的影像)

步驟 4:將授權規則套用至類別和方法

在步驟 3 中,我們不允許匿名使用者檢視檔案的內容,並禁止所有使用者但 Tito 刪除檔案。 這是透過宣告式和程式設計技術,隱藏未經授權的訪客相關聯的使用者介面元素來完成。 在我們的簡單範例中,正確隱藏使用者介面元素很簡單,但更複雜的網站可能有許多不同的方式可執行相同的功能呢? 在將該功能限制為未經授權的使用者時,如果我們忘記隱藏或停用所有適用的使用者介面元素,會發生什麼事?

確保未經授權的使用者無法存取特定功能片段的簡單方式,就是使用PrincipalPermission 屬性裝飾該類別或方法。 當 .NET 執行時間使用類別或執行其中一個方法時,它會檢查以確保目前的安全性內容具有使用類別或執行方法的許可權。 屬性 PrincipalPermission 提供一種機制,讓我們可以定義這些規則。

讓我們示範如何使用 PrincipalPermission GridView SelectedIndexChangedRowDeleting 事件處理常式上的 屬性,分別禁止 Tito 以外的匿名使用者和使用者執行。 我們只需要在每個函式定義上新增適當的屬性:

[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
    ...
}

[PrincipalPermission(SecurityAction.Demand, Name="Tito")]
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    ...
}

事件處理常式的 SelectedIndexChanged 屬性會指示只有已驗證的使用者可以執行事件處理常式,因為事件處理常式上的 RowDeleting 屬性會將執行限制為 Tito。

如果 Tito 以外的使用者嘗試執行 RowDeleting 事件處理常式,或非驗證的使用者嘗試執行 SelectedIndexChanged 事件處理常式,則 .NET 執行時間會引發 SecurityException

如果安全性內容未獲授權執行方法,則會擲回 SecurityException

圖 14:如果安全性內容未獲授權執行方法,則會 SecurityException 擲回 (Click 以檢視大小完整的映射)

注意

若要允許多個安全性內容存取類別或方法,請使用每個安全性內容的屬性裝飾類別或方法 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 授權,要求身分識別有權存取特定資源。 不過,許多案例都會呼叫更精細的授權規則。 我們不需要定義允許存取頁面的人員,可能需要讓每個人都存取頁面,但視流覽頁面的使用者而定,顯示不同的資料或提供不同的功能。 頁面層級授權通常牽涉到隱藏特定使用者介面元素,以防止未經授權的使用者存取禁止的功能。 此外,也可以使用屬性來限制對類別的存取,以及針對特定使用者執行其方法。

快樂的程式設計!

深入閱讀

如需本教學課程中討論之主題的詳細資訊,請參閱下列資源:

關於作者

Scott Mitchell 是多個 ASP/ASP.NET 書籍的作者,且 4GuysFromRolla.com 的作者,自 1998 年以來,已與 Microsoft Web 技術合作。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以在 或 透過在 的 http://ScottOnWriting.NET 部落格連線 mitchell@4guysfromrolla.com 到 Scott。

特別感謝

本教學課程系列是由許多實用的檢閱者檢閱。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com 放在 。