窗體驗證概觀 (C#)

作者:Scott Mitchell

注意

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

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

下載程式代碼下載 PDF

在本教學課程中,我們將從只討論到實作;特別是,我們將探討如何實作窗體驗證。 我們在本教學課程中開始建構的 Web 應用程式將會繼續建立在後續教學課程中,因為我們從簡單的窗體驗證移至成員資格和角色。

如需本主題的詳細資訊,請參閱這段影片: 在 ASP.NET 中使用基本窗體驗證

簡介

上述教學課程 中,我們已討論 ASP.NET 所提供的各種驗證、授權和用戶帳戶選項。 在本教學課程中,我們將從只討論到實作;特別是,我們將探討如何實作窗體驗證。 我們在本教學課程中開始建構的 Web 應用程式將會繼續建立在後續教學課程中,因為我們從簡單的窗體驗證移至成員資格和角色。

本教學課程從深入探討窗體驗證工作流程開始,這是我們在上一個教學課程中所接觸的主題。 接下來,我們將建立 ASP.NET 網站,以示範窗體驗證的概念。 接下來,我們會將網站設定為使用窗體驗證、建立簡單的登入頁面,以及瞭解如何在程式代碼中判斷使用者是否已通過驗證,如果是的話,他們登入的用戶名稱。

瞭解窗體驗證工作流程、在 Web 應用程式中啟用它,以及建立登入和註銷頁面,都是建置支援使用者帳戶並透過網頁驗證使用者之 ASP.NET 應用程式的重要步驟。 因此,由於這些教學課程是彼此建置的,因此建議您先完整完成本教學課程,再繼續進行下一個教學課程,即使您在過去專案中已有設定窗體驗證的經驗也一樣。

瞭解窗體驗證工作流程

當 ASP.NET 運行時間處理 ASP.NET 資源的要求時,例如 ASP.NET 頁面或 ASP.NET Web 服務,要求會在生命週期期間引發一些事件。 在要求的開頭和結尾引發事件、要求經過驗證和授權時引發的事件、在未處理的例外狀況案例中引發的事件等等。 若要查看事件的完整清單,請參閱 HttpApplication 物件的事件

HTTP 模組 是Managed類別,其程式代碼會執行,以回應要求生命週期中的特定事件。 ASP.NET 隨附許多 HTTP 模組,這些模組會在幕後執行基本工作。 與討論特別相關的兩個內建 HTTP 模組如下:

  • FormsAuthenticationModule – 藉由檢查窗體驗證票證來驗證使用者,這通常包含在使用者的 Cookies 集合中。 如果沒有窗體驗證票證存在,則使用者是匿名的。
  • UrlAuthorizationModule – 判斷目前使用者是否有權存取要求的 URL。 此課程模組會藉由諮詢應用程式組態檔中指定的授權規則來判斷授權單位。 ASP.NET 也包含 FileAuthorizationModule ,藉由查詢要求的檔案 () ACL 來判斷授權單位。

嘗試FormsAuthenticationModule在執行 (和 FileAuthorizationModule) 之前UrlAuthorizationModule驗證使用者。 如果提出要求的使用者未獲授權存取要求的資源,授權模組會終止要求,並傳回 HTTP 401 未經授權 狀態。 在 Windows 驗證 案例中,HTTP 401 狀態會傳回瀏覽器。 此狀態代碼會導致瀏覽器透過強制回應對話框提示使用者輸入其認證。 不過,使用窗體驗證時,HTTP 401 未經授權狀態永遠不會傳送至瀏覽器,因為 FormsAuthenticationModule 會偵測到此狀態,並修改它以改為透過 HTTP 302 重新導向狀態將使用者重新導向至登入頁面, (而不是透過 HTTP 302 重新 導向狀態) 。

登入頁面的責任是判斷用戶的認證是否有效,如果是的話,若要建立窗體驗證票證,並將使用者重新導向回他們嘗試瀏覽的頁面。 驗證票證包含在網站的後續要求中,用來 FormsAuthenticationModule 識別使用者。

窗體驗證工作流程

圖 1:窗體驗證工作流程

記住跨頁面流覽的驗證票證

登入之後,窗體驗證票證必須在每個要求上傳回至網頁伺服器,讓使用者在瀏覽網站時仍會保持登入狀態。 這通常是藉由將驗證票證放在使用者的Cookie集合中來完成。 Cookie 是位於用戶電腦上的小型文本檔,而且會在每個要求上的 HTTP 標頭中傳輸至建立 Cookie 的網站。 因此,一旦窗體驗證票證建立並儲存在瀏覽器的 Cookie 中,每個後續造訪該網站都會傳送驗證票證以及要求,藉此識別使用者。

Cookie 的其中一個層面是其到期日,也就是瀏覽器捨棄 Cookie 的日期和時間。 當窗體驗證 Cookie 過期時,使用者就無法再進行驗證,因此變成匿名。 當使用者從公用終端機流覽時,他們可能希望其驗證票證在關閉瀏覽器時到期。 不過,從家瀏覽時,相同的使用者可能會想要在瀏覽器重新啟動時記住驗證票證,如此一來,他們就不需要每次造訪網站時重新登入。 此決策通常是由使用者以登入頁面上的 [記住我] 複選框的形式進行。 在步驟 3 中,我們將檢查如何實作登入頁面中的 [記住我] 複選框。 下列教學課程會詳細說明驗證票證逾時設定。

注意

使用者代理程式可能用來登入網站可能不支援 Cookie。 在這種情況下,ASP.NET 可以使用無 Cookie 窗體驗證票證。 在此模式中,驗證票證會編碼為URL。 我們將在下一個教學課程中查看何時使用 Cookie 無驗證票證及其建立和管理方式。

窗體驗證的範圍

FormsAuthenticationModule是屬於 ASP.NET 運行時間一部分的 Managed 程式代碼。 在 Microsoft 的 Internet Information Services 版本 7 (IIS) Web 伺服器之前,IIS 的 HTTP 管線與 ASP.NET 運行時間管線之間有不同的屏障。 簡單來說,在 IIS 6 和更早版本中, FormsAuthenticationModule 唯一會在將要求從 IIS 委派給 ASP.NET 運行時間時執行。 根據預設,IIS 會處理靜態內容本身,例如 HTML 頁面和 CSS 和影像檔案,而且只有在要求擴展名為 .aspx、.asmx 或 .ashx 的頁面時,才會對 ASP.NET 運行時間提出要求。

不過,IIS 7 允許整合的 IIS 和 ASP.NET 管線。 透過幾個組態設定,您可以設定 IIS 7 來叫用 所有 要求的 FormsAuthenticationModule。 此外,使用 IIS 7,您可以為任何類型的檔案定義 URL 授權規則。 如需詳細資訊,請參閱 IIS6 與 IIS7 安全性之間的變更您的 Web 平台安全性,以及 瞭解 IIS7 URL 授權

在 IIS 7 之前的版本中,長本文簡短說明,您只能使用窗體驗證來保護 ASP.NET 運行時間所處理的資源。 同樣地,URL 授權規則只會套用至 ASP.NET 運行時間所處理的資源。 但是,使用 IIS 7,可以將 FormsAuthenticationModule 和 UrlAuthorizationModule 整合到 IIS 的 HTTP 管線中,藉此將這項功能延伸至所有要求。

步驟 1:為本教學課程系列建立 ASP.NET 網站

為了達到最廣泛的對象,我們將在此系列中建置的 ASP.NET 網站,將會使用 Microsoft 的免費 Visual Studio 2008、 Visual Web Developer 2008 版本來建立。 我們會在 Microsoft SQL Server 2005 Express Edition 資料庫中實SqlMembershipProvider作使用者存放區。 如果您使用 Visual Studio 2005 或不同版本的 Visual Studio 2008 或 SQL Server,別擔心 - 這些步驟幾乎完全相同,而且將會指出任何非簡單差異。

注意

每個教學課程中使用的示範 Web 應用程式都可下載。 此可下載的應用程式是以 Visual Web Developer 2008 為目標,以 .NET Framework 3.5 版為目標。 由於應用程式是以 .NET 3.5 為目標,因此其 Web.config 檔案包含額外的 3.5 特定組態元素。 簡短說明,如果您尚未在計算機上安裝 .NET 3.5,則可下載的Web應用程式將無法運作,而不需要先從 Web.config 移除3.5特定標記。

在設定窗體驗證之前,我們必須先有 ASP.NET 網站。 首先,建立新的文件系統型 ASP.NET 網站。 若要完成此動作,請啟動Visual Web Developer,然後移至 [檔案] 選單,然後選擇 [新增網站],並顯示 [新增網站] 對話框。 選擇 ASP.NET 網站樣本、將 [位置] 下拉式清單設定為 [檔案系統]、選擇要放置網站的資料夾,並將語言設定為 C# 。 這會建立具有Default.aspx ASP.NET 頁面、App_Data資料夾和 Web.config 檔案的新網站。

注意

Visual Studio 支援兩種專案管理模式:網站專案和 Web 應用程式專案。 網站專案缺少項目檔,而 Web 應用程式專案則模擬 Visual Studio .NET 2002/2003 中的項目架構 – 它們包含項目檔,並將專案的原始程式碼編譯成單一元件,該元件會放在 /bin 資料夾中。 Visual Studio 2005 一開始只支援網站專案,雖然 Web 應用程式專案模型是以 Service Pack 1 重新引進;Visual Studio 2008 提供這兩個專案模型。 不過,Visual Web Developer 2005 和 2008 版本僅支援網站專案。 我將會使用網站專案模型。 如果您使用非 Express 版本,而想要改用 Web 應用程式專案模型 ,請放心地這麼做,但請注意,您在畫面上看到的內容與您必須採取的步驟與這些教學課程中提供的指示之間可能會有一些差異。

建立新檔案 System-Based 網站

圖 2:建立新的檔案 System-Based 網站 (按兩下以檢視大小完整的映像)

新增主版頁面

接下來,將新的主版頁面新增至根目錄中名為 Site.master 的網站。 主版頁面 可讓頁面開發人員定義可套用至 ASP.NET 頁面的全網站範本。 主版頁面的主要優點是網站的整體外觀可以定義在單一位置,藉此輕鬆地更新或調整網站的版面配置。

將名為 Site.master 的主版頁面新增至網站

圖 3:將名為 Site.master 的主版頁面新增至網站 (按兩下即可檢視完整大小的影像)

在主版頁面中定義全網站版面配置。 您可以使用 [設計] 檢視,並新增您需要的任何 [版面配置] 或 [Web] 控件,也可以手動在 [來源] 檢視中手動新增標記。 我已建構主版頁面的版面配置,以模擬在 ASP.NET 2.0 教學 課程系列中使用的版面配置, (請參閱圖 4) 。 主版頁面會使用 級聯樣式表 來定位和樣式與檔案中所定義的 CSS 設定,Style.css (本教学课程的相关下载) 。 雖然您無法從如下所示的標記中分辨,但會定義 CSS 規則,讓導覽 <div> 的內容絕對定位,使其出現在左側,且寬度固定為 200 像素。

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="Site.master.cs" Inherits="Site" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Forms Authentication, Authorization, and User Accounts</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <div id="wrapper">
        <form id="form1" runat="server">
        
            <div id="header">
                <span class="title">User Account Tutorials</span>
            </div>
        
            <div id="content">
                <asp:contentplaceholder id="MainContent" runat="server">
                  <!-- Page-specific content will go here... -->
                </asp:contentplaceholder>
            </div>
            
            <div id="navigation">
                TODO: Menu will go here...
            </div>
        </form>
    </div>
</body>
</html>

主版頁面會定義靜態頁面配置,以及可使用主版頁面的 ASP.NET 頁面編輯的區域。 這些內容可編輯區域是由 ContentPlaceHolder 控件表示,這可以在內容 <div> 中看見。 我們的主版頁面有單 ContentPlaceHolder 一 (MainContent) ,但主版頁面可能有多個 ContentPlaceHolders。

在上方輸入標記時,切換至 [設計] 檢視會顯示主版頁面的版面配置。 使用此主版頁面的任何 ASP.NET 頁面都會有這個統一的版面配置,並能夠指定區域的標記 MainContent

主版頁面,透過設計檢視檢視檢視時

圖 4:主版頁面,透過 [設計視圖] 檢視時 (按兩下即可檢視完整大小的影像)

建立內容頁面

此時,我們在網站中有一個Default.aspx頁面,但它不會使用我們剛才建立的主版頁面。 雖然可以操作網頁的宣告式標記以使用主版頁面,但如果頁面未包含任何內容,則只要刪除頁面並重新新增至專案會比較容易,並指定要使用的主版頁面。 因此,請從從專案刪除Default.aspx開始。

接下來,以滑鼠右鍵按兩下 方案總管中的項目名稱,然後選擇新增名為 Default.aspx 的新Web Form。 這次,核取 [選取主版頁面] 複選框,然後從清單中選擇 Site.master 主版頁面。

新增Default.aspx頁面選擇選取主版頁面

圖 5:新增新的Default.aspx頁面選擇選取主版頁面 (按兩下即可檢視完整大小的影像)

使用 Site.master 主版頁面

圖 6:使用 Site.master 主版頁面

注意

如果您使用 Web 應用程式專案模型,[新增專案] 對話框不包含 [選取主版頁面] 複選框。 相反地,您必須新增類型為「Web 內容表單」的專案。選擇 [Web 內容表單] 選項並按兩下 [新增] 之後,Visual Studio 會顯示相同的 [選取主圖形] 對話框,如圖 6 所示。

新的Default.aspx頁面宣告式標記只 @Page 包含指示詞,指定主版頁面檔案的路徑,以及主版頁面 MainContent ContentPlaceHolder 的內容控件。

<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
</asp:Content>

目前,請將Default.aspx空白。 我們稍後會在本教學課程中返回以新增內容。

注意

我們的主版頁面包含功能表或一些其他導覽介面的區段。 我們將在未來的教學課程中建立這類介面。

步驟 2:啟用窗體驗證

建立 ASP.NET 網站后,下一項工作是啟用窗體驗證。 應用程式的驗證組態是透過 <authentication> Web.config 中的 元素來指定。元素<authentication>包含單一屬性具名模式,指定應用程式所使用的驗證模型。 此屬性可以有下列四個值之一:

  • Windows – 如上一個教學課程所述,當應用程式使用 Windows 驗證 網頁伺服器負責驗證訪客,這通常是透過基本、摘要或整合式 Windows 驗證 來完成。
  • 表單 – 用戶會透過網頁上的表單進行驗證。
  • Passport – 使用者會使用 Microsoft 的 Passport 網路進行驗證。
  • – 未使用任何驗證模型;所有訪客都是匿名的。

根據預設,ASP.NET 應用程式會使用 Windows 驗證。 若要將驗證類型變更為窗體驗證,則我們需要將專案的mode屬性修改 <authentication> 為 Forms。

如果您的專案尚未包含 Web.config 檔案,請立即以滑鼠右鍵按兩下 方案總管中的項目名稱,選擇 [新增專案],然後新增 Web 組態檔。

如果您的專案尚未包含 Web.config,請立即新增

圖 7:如果您的專案尚未包含 Web.config,請立即新增它 (按兩下以檢視完整大小的影像)

接下來,找出 元素 <authentication> 並加以更新,以使用窗體驗證。 在此變更之後,您的 Web.config 檔案標記看起來應該如下所示:

<configuration>
    <system.web>
        ... Unrelated configuration settings and comments removed for brevity ...
        <!--
            The <authentication> section enables configuration 
            of the security authentication mode used by 
            ASP.NET to identify an incoming user. 
        -->
        <authentication mode="Forms" />
    </system.web>
</configuration>

注意

因為 Web.config 是 XML 檔案,所以大小寫很重要。 請務必使用大寫 「F」 將 mode 屬性設定為 Forms。 如果您使用不同的大小寫,例如「表單」,當您透過瀏覽器瀏覽網站時,將會收到設定錯誤。

元素 <authentication> 可以選擇性地包含 <forms> 包含窗體驗證特定設定的子元素。 現在,讓我們只使用預設窗體驗證設定。 我們將在下一個教學課程中更詳細地探索 <forms> 子元素。

步驟 3:建置登入頁面

為了支援窗體驗證,網站需要登入頁面。 如「瞭解窗體驗證工作流程」一節所述,如果用戶嘗試存取未獲授權檢視的頁面, FormsAuthenticationModule 會自動將使用者重新導向至登入頁面。 另外還有 ASP.NET Web 控件會顯示登入頁面的連結給匿名使用者。 這表示「登入頁面的 URL 為何?」

根據預設,窗體驗證系統預期登入頁面命名為 Login.aspx,並放在 Web 應用程式的根目錄中。 如果您想要使用不同的登入頁面 URL,您可以在 Web.config 中指定它。我們將在後續教學課程中看到如何執行這項操作。

登入頁面有三個責任:

  1. 提供可讓訪客輸入其認證的介面。
  2. 判斷提交的認證是否有效。
  3. 藉由建立窗體驗證票證來「登入」使用者。

建立登入頁面的使用者介面

讓我們開始使用第一個工作。 將新的 ASP.NET 頁面新增至名為 Login.aspx 的網站根目錄,並將其與 Site.master 主版頁面產生關聯。

新增名為 Login.aspx 的新 ASP.NET 頁面

圖 8:新增名為 ASP.NET 頁面Login.aspx (按鍵即可檢視完整大小的影像)

一般的登入頁面介面包含兩個文本框–一個用於使用者的名稱、一個用於其密碼,另一個用於提交窗體的按鈕。 網站通常會包含 「記住我」複選框,如果核取,則會在瀏覽器重新啟動時保存產生的驗證票證。

將兩個 TextBox 新增至 Login.aspx,並將其屬性分別設定 ID 為 UserName 和 Password。 同時將Password的 TextMode 屬性設定為Password。 接下來,新增 CheckBox 控件,並將其 ID 屬性設定為 RememberMe,並將其 Text 屬性設定為 “Remember Me”。 接著,新增名為 LoginButton 的 Button,其 Text 屬性設定為 “Login”。 最後,新增標籤 Web 控件,並將其 ID 屬性設定為 InvalidCredentialsMessage,並將其 Text 屬性設定為 「您的使用者名稱或密碼無效。 請再試一次。」,將其 ForeColor 屬性設為 Red,並將其 Visible 屬性設為 False。

此時,您的畫面看起來應該類似圖 9 中的螢幕快照,而頁面的宣告式語法應該如下所示:

<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
    <h1>
        Login</h1>
    <p>
        Username:
        <asp:TextBox ID="UserName" runat="server"></asp:TextBox></p>
    <p>
        Password:
        <asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox></p>
    <p>
        <asp:CheckBox ID="RememberMe" runat="server" Text="Remember Me" /> </p>
    <p>
        <asp:Button ID="LoginButton" runat="server" Text="Login" OnClick="LoginButton_Click" /> </p>
    <p>
        <asp:Label ID="InvalidCredentialsMessage" runat="server" ForeColor="Red" Text="Your username or password is invalid. Please try again."
            Visible="False"></asp:Label> </p>
</asp:Content>

登入頁面包含兩個 TextBox、CheckBox、按鈕和標籤

圖 9:登入頁面包含兩個 TextBox、CheckBox、按鈕和標籤 (按兩下即可檢視全大小影像)

最後,為 LoginButton 的 Click 事件建立事件處理程式。 從 Designer,只要按兩下Button控制項即可建立此事件處理程式。

判斷提供的認證是否有效

我們現在需要在 Button 的 Click 事件處理程式中實作工作 2 – 判斷提供的認證是否有效。 若要這樣做,必須有保留所有使用者認證的使用者存放區,以便我們可以判斷提供的認證是否符合任何已知認證。

在 ASP.NET 2.0 之前,開發人員必須負責實作自己的使用者存放區,以及撰寫程式代碼來驗證針對市集提供的認證。 大部分開發人員都會在資料庫中實作使用者存放區,建立名為Users的數據表,其中包含UserName、Password、Email、LastLoginDate 等數據行。 然後,此數據表會為每個用戶帳戶有一筆記錄。 確認使用者提供的認證會牽涉到查詢資料庫是否有相符的使用者名稱,然後確保資料庫中的密碼對應至提供的密碼。

使用 ASP.NET 2.0,開發人員應該使用其中一個成員資格提供者來管理使用者存放區。 在本教學課程系列中,我們將使用 SqlMembershipProvider,其會針對使用者存放區使用 SQL Server 資料庫。 使用 SqlMembershipProvider 時,我們需要實作特定的資料庫架構,其中包含提供者預期的數據表、檢視和預存程式。 我們將探討如何在 SQL Server 教學課程中建立成員資格架構中實作此架構。 有了成員資格提供者,驗證使用者的認證就如同呼叫 Membership 類別ValidateUser (usernamepassword) 方法一樣簡單,它會傳回 Boolean 值,指出使用者名稱和密碼組合是否有效。 當我們尚未實作 SqlMembershipProvider 的使用者存放區時,我們目前無法使用 Membership 類別的 ValidateUser 方法。

我們實作 SqlMembershipProvider) 之後,不需要花時間建置自己的自定義 Users 資料庫數據表 (,而是改為將登入頁面本身的有效認證硬式編碼。 在 LoginButton 的 Click 事件處理程式中,新增下列程式代碼:

protected void LoginButton_Click(object sender, EventArgs e)
{
    // Three valid username/password pairs: Scott/password, Jisun/password, and Sam/password.
    string[] users = { "Scott", "Jisun", "Sam" };
    string[] passwords = { "password", "password", "password" };
    for (int i = 0; i < users.Length; i++)
    {
        bool validUsername = (string.Compare(UserName.Text, users[i], true) == 0);
        bool validPassword = (string.Compare(Password.Text, passwords[i], false) == 0);
        if (validUsername && validPassword)
        {
            // TODO: Log in the user...
            // TODO: Redirect them to the appropriate page
        }
    }
    // If we reach here, the user's credentials were invalid
    InvalidCredentialsMessage.Visible = true;
}

如您所見,有三個有效的用戶帳戶 – Scott、Jisun 和 Sam ,而這三個帳戶都有相同的密碼 (“password”) 。 程式代碼會迴圈查看尋找有效使用者名稱和密碼相符專案的用戶和密碼陣列。 如果使用者名稱和密碼都有效,我們需要登入用戶,然後將其重新導向至適當的頁面。 如果認證無效,則會顯示 InvalidCredentialsMessage 標籤。

當使用者輸入有效的認證時,我提到他們接著會重新導向至「適當的頁面」。不過,適當的頁面為何? 回想一下,當用戶流覽未獲授權檢視的頁面時,FormsAuthenticationModule 會自動將它們重新導向至登入頁面。 如此一來,它會透過 ReturnUrl 參數在 querystring 中包含要求的 URL。 也就是說,如果使用者嘗試造訪ProtectedPage.aspx,而且他們未獲授權這麼做,FormsAuthenticationModule 會將他們重新導向至:

Login.aspx?ReturnUrl=ProtectedPage.aspx

成功登入時,應該將使用者重新導向回ProtectedPage.aspx。 或者,用戶可以自行流覽登入頁面。 在此情況下,在登入用戶之後,應該將他們傳送至根資料夾的 Default.aspx 頁面。

登入使用者

假設提供的認證有效,我們需要建立窗體驗證票證,藉此登入網站。 System.Web.Security 命名空間中的 FormsAuthentication 類別提供各種方法來透過窗體驗證系統登入和註銷使用者。 雖然 FormsAuthentication 類別中有數種方法,但我們在此階段感興趣的三種方法如下:

  • GetAuthCookie (usernamepersistCookie) – 為提供的名稱 使用者名稱建立窗體驗證票證。 接下來,這個方法會建立並傳回保存驗證票證內容的 HttpCookie 物件。 如果 persistCookie 為 true,則會建立持續性Cookie。
  • SetAuthCookie (usernamepersistCookie) – 呼叫 GetAuthCookie (usernamepersistCookie) 方法來產生窗體驗證 Cookie。 此方法接著會將 GetAuthCookie 傳回的 Cookie 新增至 Cookies 集合, (假設正在使用 Cookie 型窗體驗證;否則,這個方法會呼叫處理無 Cookie 票證邏輯) 的內部類別。
  • RedirectFromLoginPage (usernamepersistCookie) – 這個方法會呼叫 SetAuthCookie (usernamepersistCookie) ,然後將使用者重新導向至適當的頁面。

當您在將 Cookie 寫出至 Cookies 集合之前,需要修改驗證票證時,GetAuthCookie 會很有用。 如果您想要建立窗體驗證票證,並將它新增至 Cookies 集合,但不想將使用者重新導向至適當的頁面,SetAuthCookie 會很有用。 或許您想要將它們保留在登入頁面上,或將它們傳送至一些替代頁面。

由於我們想要登入使用者,並將其重新導向至適當的頁面,因此讓我們使用 RedirectFromLoginPage。 更新 LoginButton 的 Click 事件處理程式,以下列程式代碼行取代兩個已加上批注的 TODO 行:

FormsAuthentication.RedirectFromLoginPage (UserName.Text, RememberMe.Checked) ;

建立窗體驗證票證時,我們會針對窗體驗證票證 使用者名稱 參數使用UserName TextBox的 Text 屬性,以及 persistCookie 參數之 RememberMe CheckBox 的已核取狀態。

若要測試登入頁面,請在瀏覽器中流覽。 首先輸入無效的認證,例如 「Nope」 的用戶名稱,以及密碼為 「wrong」。。 按兩下 [登入] 按鈕時,就會發生回傳,並顯示InvalidCredentialsMessage標籤。

輸入無效的認證時,會顯示 InvalidCredentialsMessage 標籤

圖 10:輸入無效的認證時會顯示 InvalidCredentialsMessage 標籤, (按兩下即可檢視完整大小的影像)

接下來,輸入有效的認證,然後按兩下 [登入] 按鈕。 這次當回傳發生時,會建立窗體驗證票證,而且會自動重新導向回Default.aspx。 此時,您已登入網站,雖然沒有視覺提示可指出您目前已登入。 在步驟 4 中,我們將瞭解如何以程式設計方式判斷使用者是否已登入,以及如何識別瀏覽頁面的使用者。

步驟 5 會檢查將用戶註銷網站的技術。

保護登入頁面

當使用者輸入其認證並提交登入頁面窗體時,認證會以 純文本透過因特網傳輸至網頁伺服器。 這表示任何駭客探查網路流量都可以看到用戶名稱和密碼。 若要避免這種情況,請務必使用 安全套接字層 (SSL) 加密網路流量。 這可確保認證 (以及整個頁面的 HTML 標記) 在離開瀏覽器之前加密,直到網頁伺服器收到這些認證為止。

除非您的網站包含敏感性資訊,否則您只需要在登入頁面上使用SSL,以及在用戶密碼會透過純文本透過網路傳送的其他頁面上使用SSL。 您不需要擔心保護窗體驗證票證,因為根據預設,它會加密和數位簽名 (,以防止竄改) 。 下列教學課程提供窗體驗證票證安全性的更徹底討論。

注意

許多財務和醫療網站都設定為 在所有可供已 驗證的使用者存取的頁面上使用 SSL。 如果您要建置這類網站,您可以設定窗體驗證系統,讓窗體驗證票證只會透過安全連線傳輸。

步驟 4:偵測已驗證的訪客並判斷其身分識別

此時,我們已啟用窗體驗證並建立一個基本的登入頁面,但我們尚未檢查如何判斷使用者是否已通過驗證或匿名。 在某些情況下,我們可能會想要根據已驗證或匿名使用者正在瀏覽頁面來顯示不同的數據或資訊。 此外,我們通常必須知道已驗證使用者的身分識別。

讓我們增強現有的Default.aspx頁面,以說明這些技術。 在 Default.aspx新增兩個 Panel 控件,一個名為 AuthenticatedMessagePanel,另一個名為 AnonymousMessagePanel。 在第一個面板中新增名為 WelcomeBackMessage 的標籤控制件。 第二個面板中新增 HyperLink 控制件,將其 Text 屬性設定為 「Log In」,並將 NavigateUrl 屬性設定為 “~/Login.aspx”。 此時,Default.aspx的宣告式標記看起來應該如下所示:

<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
    <asp:Panel runat="server" ID="AuthenticatedMessagePanel">
        <asp:Label runat="server" ID="WelcomeBackMessage"></asp:Label>
    </asp:Panel>
    
    <asp:Panel runat="Server" ID="AnonymousMessagePanel">
        <asp:HyperLink runat="server" ID="lnkLogin" Text="Log In" NavigateUrl="~/Login.aspx"></asp:HyperLink>
    </asp:Panel>
</asp:Content>

因為您現在可能猜到,這裡的想法就是只向已驗證的訪客顯示 AuthenticatedMessagePanel,而只向匿名訪客顯示 AnonymousMessagePanel。 若要達成此目的,我們需要根據使用者是否已登入來設定這些面板的可見屬性。

Request.IsAuthenticated 屬性會傳回布爾值,指出是否已驗證要求。 在 Page_Load 事件處理程式程式代碼中輸入下列程式代碼:

protected void Page_Load(object sender, EventArgs e)
{
    if (Request.IsAuthenticated)
    {
        WelcomeBackMessage.Text = "Welcome back!";
    
        AuthenticatedMessagePanel.Visible = true;
        AnonymousMessagePanel.Visible = false;
    }
    else
    {
        AuthenticatedMessagePanel.Visible = false;
        AnonymousMessagePanel.Visible = true;
    }
}

在此程式代碼就緒后,請流覽Default.aspx瀏覽器。 假設您尚未登入,您會看到登入頁面的連結, (請參閱圖 11) 。 按兩下此連結並登入網站。 如我們在步驟 3 中所見,輸入認證之後,您會回到Default.aspx,但這次頁面會顯示「歡迎回來!」訊息, (請參閱圖 12) 。

匿名流覽時,會顯示登入連結

圖 11:匿名瀏覽時,會顯示登入連結

已驗證的用戶會顯示

圖 12:已驗證的用戶會顯示「歡迎回來!」消息

我們可以透過 HttpContext 物件的User 屬性來判斷目前登入使用者的身分識別。 HttpContext 物件代表目前要求的相關信息,而且是這類常見 ASP.NET 物件的主資料夾,例如回應、要求和會話等等。 User 屬性代表目前 HTTP 要求的安全性內容,並實 作 IPrincipal 介面

User 屬性是由 FormsAuthenticationModule 所設定。 具體而言,當 FormsAuthenticationModule 在傳入要求中找到窗體驗證票證時,它會建立新的 GenericPrincipal 物件,並將它指派給 User 屬性。

主要物件 (,例如 GenericPrincipal) 提供使用者身分識別及其所屬角色的相關信息。 IPrincipal 介面會定義兩個成員:

我們可以使用下列程式代碼來判斷目前訪客的名稱:

string currentUsersName = User.Identity.Name;

使用窗體驗證時,會為 GenericPrincipal 的 Identity 屬性建立 FormsIdentity 物件 。 FormsIdentity 類別一律會傳回其 AuthenticationType 屬性的字串 “Forms”,而它的 IsAuthenticated 屬性則傳回 true。 Name 屬性會傳回建立窗體驗證票證時所指定的用戶名稱。 除了這三個屬性之外,FormsIdentity 還包含透過其 Ticket 屬性存取基礎驗證票證。 Ticket 屬性會傳回 FormsAuthenticationTicket 類型的物件,其具有 Expiration、IsPersistent、IssueDate、Name 等屬性。

此處要取用的重點是 FormsAuthentication.GetAuthCookie (usernamepersistCookie) 、FormsAuthentication.SetAuthCookie (username、persistCookie) 和 FormsAuthentication.RedirectFromLoginPage (usernamepersistCookie) 方法中所傳回的值相同 User.Identity.Name。 此外,透過將User.Identity轉換成 FormsIdentity 對象,然後存取 Ticket 屬性,即可使用這些方法建立的驗證票證:

FormsIdentity ident = User.Identity as FormsIdentity;
FormsAuthenticationTicket authTicket = ident.Ticket;

讓我們在Default.aspx中提供更個人化的訊息。 更新Page_Load事件處理程式,以便將 WelcomeBackMessage Label 的 Text 屬性指派為字串 “Welcome back, username!”

WelcomeBackMessage.Text = “Welcome back, ” + User.Identity.Name + “!”;

圖 13 顯示以使用者 Scott) 身分登入時,此修改 (的效果。

歡迎訊息包含目前登入的用戶名稱

圖 13:歡迎訊息包含目前登入的用戶名稱

使用 LoginView 和 LoginName 控件

向已驗證和匿名用戶顯示不同的內容是常見的需求;因此會顯示目前登入用戶的名稱。 因此,ASP.NET 包含兩個提供圖 13 所示相同功能的 Web 控件,但不需要撰寫單行程式代碼。

LoginView 控件是以範本為基礎的 Web 控件,可讓您輕鬆地對已驗證和匿名用戶顯示不同的數據。 LoginView 包含兩個預先定義的範本:

  • AnonymousTemplate – 新增至此範本的任何標記只會顯示給匿名訪客。
  • LoggedInTemplate – 此範本的標記只會向已驗證的用戶顯示。

讓我們將 LoginView 控件新增至網站的主版頁面 Site.master。 不過,我們不要只新增 LoginView 控件,而是新增 ContentPlaceHolder 控件,然後將 LoginView 控件放在該新的 ContentPlaceHolder 內。 此決策的理由很快就會變得明顯。

注意

除了 AnonymousTemplate 和 LoggedInTemplate 之外,LoginView 控制項還可以包含角色特定的範本。 角色特定範本只會向屬於指定角色的用戶顯示標記。 我們將在未來的教學課程中檢查 LoginView 控件的角色型功能。

首先,將名為LoginContent的 ContentPlaceHolder 新增至導覽 <div> 元素內的主版頁面。 您只要將 ContentPlaceHolder 控件從 [工具箱] 拖曳到 [來源] 檢視,將產生的標記放在 “TODO:Menu 將移至這裡...文本。

<div id="navigation">
    <asp:ContentPlaceHolder ID="LoginContent" runat="server">
    </asp:ContentPlaceHolder>
   
    TODO: Menu will go here...
</div>

接下來,在 LoginContent ContentPlaceHolder 中新增 LoginView 控件。 放置於主版頁面的 ContentPlaceHolder 控件中的內容會被視為 ContentPlaceHolder 的預設內容 。 也就是說,ASP.NET 使用此主版頁面的頁面可以為每個 ContentPlaceHolder 指定自己的內容,或使用主版頁面的默認內容。

LoginView 和其他登入相關控件位於 [工具箱的登入] 索引卷標中。

工具箱中的LoginView控件

圖 14:工具箱中的 LoginView 控件

接下來,在 LoginView 控件之後立即新增兩個 <br /> 元素,但仍在 ContentPlaceHolder 內。 此時,導覽 <div> 元素的標記看起來應該如下所示:

<div id="navigation">
    <asp:ContentPlaceHolder ID="LoginContent" runat="server">
        <asp:LoginView ID="LoginView1" runat="server">
        </asp:LoginView>
        <br /><br />
    </asp:ContentPlaceHolder>
   
    TODO: Menu will go here...
</div>

LoginView 的範本可以從 Designer 或宣告式標記定義。 從 Visual Studio 的 Designer,展開 LoginView 的智慧標記,其中會列出下拉式清單中的已設定範本。 在 AnonymousTemplate 文字中輸入 “Hello, text”接下來,新增 HyperLink 控件,並將其 Text 和 NavigateUrl 屬性分別設定為 “Log In” 和 “~/Login.aspx”。

設定 AnonymousTemplate 之後,切換至 LoggedInTemplate 並輸入文字「歡迎回來」。 然後將 LoginName 控件從 [工具箱] 拖曳至 LoggedInTemplate,然後將它緊接在 “Welcome back, ” 文字後面。 LoginName 控制項如其名稱所示,會顯示目前登入用戶的名稱。 在內部,LoginName 控制項只會輸出 User.Identity.Name 屬性

將這些新增至 LoginView 的範本之後,標記看起來應該類似下列內容:

<div id="navigation">
    <asp:ContentPlaceHolder ID="LoginContent" runat="server">
        <asp:LoginView ID="LoginView1" runat="server">
            <LoggedInTemplate>
                Welcome back,
                <asp:LoginName ID="LoginName1" runat="server" />.
            </LoggedInTemplate>
            <AnonymousTemplate>
                Hello, stranger.
                <asp:HyperLink ID="lnkLogin" runat="server" NavigateUrl="~/Login.aspx">Log In</asp:HyperLink>
            </AnonymousTemplate>
        </asp:LoginView>
        
        <br /><br />
    </asp:ContentPlaceHolder>
   
    TODO: Menu will go here...
</div>

除了 Site.master 主版頁面之外,網站中的每個頁面都會根據使用者是否經過驗證來顯示不同的訊息。 圖 15 顯示使用者 Jisun 瀏覽瀏覽器時Default.aspx頁面。 「歡迎回來,Jisun」訊息重複兩次:一次是透過LoginView控件在 (主版頁面的導覽區段中,我們剛才新增了) ,一次是在Default.aspx的內容區域中 (透過面板控件和程式設計邏輯) 。

LoginView 控件會顯示

圖 15:LoginView 控件會顯示「歡迎回來,Jisun」。

由於我們已將LoginView新增至主版頁面,因此它可以出現在網站上的每一個頁面上。 不過,可能有網頁不想要顯示此訊息。 其中一個這類頁面是登入頁面,因為登入頁面的連結似乎不在該處。 由於我們在主版頁面的 ContentPlaceHolder 中放置 LoginView 控件,因此我們可以覆寫內容頁面中的這個預設標記。 開啟Login.aspx並移至 Designer。 由於我們在主版頁面中的LoginContent ContentPlaceHolder Login.aspx中未明確定義 Content 控件,因此登入頁面會顯示此 ContentPlaceHolder 的主版頁面預設標記。 您可以透過 Designer 看到此專案 – LoginContent ContentPlaceHolder 會顯示 loginView 控件 (預設標記) 。

登入頁面會顯示主版頁面的LoginContent ContentPlaceHolder的默認內容

圖 16:登入頁面顯示主版頁面的 LoginContent ContentPlaceHolder 的預設內容 (按兩下即可檢視大小完整的影像)

若要覆寫 LoginContent ContentPlaceHolder 的預設標記,只要以滑鼠右鍵按兩下 Designer中的區域,然後從操作功能表選擇 [建立自定義內容] 選項即可。 (使用Visual Studio 2008時,ContentPlaceHolder 包含智慧標記,當選取時,會提供相同的選項。) 這會將新的內容控件新增至頁面的標記,進而讓我們定義此頁面的自定義內容。 您可以在這裡新增自定義訊息,例如「請登入...」,但讓我們只保留空白。

注意

在 Visual Studio 2005 中,建立自定義內容會在 [ASP.NET] 頁面中建立空白的 Content 控件。 不過,在Visual Studio 2008中,建立自定義內容會將主版頁面的預設內容複製到新建立的內容控件。 如果您使用 Visual Studio 2008,則在建立新的內容控件之後,請務必清除從主版頁面複製的內容。

圖 17 顯示進行這項變更之後,從瀏覽器瀏覽時Login.aspx頁面。 請注意,在左側導覽 <div> 中沒有 「Hello, welcome back」 或 「Welcome back, username」 訊息,因為流覽Default.aspx時。

登入頁面會隱藏預設LoginContent ContentPlaceHolder的標記

圖 17:登入頁面會隱藏預設 LoginContent ContentPlaceHolder 的標記 (按兩下即可檢視大小完整的影像)

步驟 5:註銷

在步驟 3 中,我們查看了建置登入頁面以將使用者登入網站,但我們尚未瞭解如何註銷使用者。除了用來記錄使用者的方法之外,FormsAuthentication 類別也會提供 SignOut 方法。 SignOut 方法只會終結窗體驗證票證,藉此將用戶註銷網站。

提供註銷連結是一項常見的功能,ASP.NET 包含專為註銷用戶而設計的控件。 LoginStatus 控件 會根據使用者的驗證狀態顯示 “Login” LinkButton 或 “Logout” LinkButton。 匿名用戶會轉譯 「登入」LinkButton,而 「註銷」LinkButton 則會向已驗證的用戶顯示。 您可以透過LoginStatus的LoginText和LogoutText屬性來設定 “Login” 和 “Logout” LinkButtons 的文字。

按兩下 [登入] LinkButton 會導致回傳,而重新導向會從中發出至登入頁面。 按兩下 「Logout」 LinkButton 會導致 LoginStatus 控制項叫用 FormsAuthentication.SignOff 方法,然後將使用者重新導向至頁面。 已註銷用戶的頁面會重新導向至 ,視LogoutAction屬性而定,該屬性可以指派給下列三個值之一:

  • 重新整理 – 預設值;將使用者重新導向至他們剛才瀏覽的頁面。 如果他們剛瀏覽的頁面不允許匿名使用者,FormsAuthenticationModule 會自動將使用者重新導向至登入頁面。

您可能會想知道為何在這裡執行重新導向。 如果使用者想要留在相同的頁面上,為什麼需要明確的重新導向? 原因是單擊 「Logoff」 LinkButton 時,使用者在其 Cookie 集合中仍然具有窗體驗證票證。 因此,回傳要求是已驗證的要求。 LoginStatus 控件會呼叫 SignOut 方法,但會在 FormsAuthenticationModule 驗證使用者之後發生。 因此,明確重新導向會導致瀏覽器重新要求頁面。 在瀏覽器重新要求頁面時,窗體驗證票證已經移除,因此傳入要求是匿名的。

  • 重新導向 – 用戶會重新導向至 LoginStatus 的 LogoutPageUrl 屬性所指定的 URL。
  • RedirectToLoginPage – 系統會將使用者重新導向至登入頁面。

讓我們將 LoginStatus 控制項新增至主版頁面,並將其設定為使用 [重新導向] 選項,將使用者傳送至顯示確認已註銷訊息的頁面。首先,在名為 Logout.aspx 的根目錄中建立頁面。 別忘了將此頁面與 Site.master 主版頁面產生關聯。 接下來,在頁面的標記中輸入訊息,說明他們已註銷的使用者。

接下來,返回 Site.master 主版頁面,並在 LoginContent ContentPlaceHolder 的 LoginView 下方新增 LoginStatus 控件。 將 LoginStatus 控件的 LogoutAction 屬性設定為 Redirect,並將其 LogoutPageUrl 屬性設定為 “~/Logout.aspx”。

<div id="navigation">
    <asp:ContentPlaceHolder ID="LoginContent" runat="server">
        <asp:LoginView ID="LoginView1" runat="server">
            <LoggedInTemplate>
                Welcome back,
                <asp:LoginName ID="LoginName1" runat="server" />.
            </LoggedInTemplate>
            <AnonymousTemplate>
                Hello, stranger.
                <asp:HyperLink ID="lnkLogin" runat="server" NavigateUrl="~/Login.aspx">Log In</asp:HyperLink>
            </AnonymousTemplate>
        </asp:LoginView>
        <br />
        <asp:LoginStatus ID="LoginStatus1" runat="server" LogoutAction="Redirect" LogoutPageUrl="~/Logout.aspx" />
        
        <br /><br />
    </asp:ContentPlaceHolder>
   
    TODO: Menu will go here...
</div>

由於 LoginStatus 不在 LoginView 控件之外,因此會同時顯示匿名和已驗證的使用者,但這沒問題,因為 LoginStatus 會正確顯示 “Login” 或 “Logout” LinkButton。 新增 LoginStatus 控件時,AnonymousTemplate 中的 “Log In” HyperLink 是多餘的,因此請將其移除。

圖 18 顯示 Jisun 造訪時Default.aspx。 請注意,左欄會顯示訊息「歡迎回來,Jisun」以及註銷的連結。單擊註銷 LinkButton 會導致回傳、將 Jisun 註銷系統,然後將她重新導向至Logout.aspx。 如圖 19 所示,在 Jisun 到達Logout.aspx已註銷,因此是匿名的。 因此,左側數據行會顯示文字「歡迎」、「歡迎」、「歡迎」,以及登入頁面的連結。

Default.aspx顯示

圖 18:Default.aspx顯示 “Welcome Back, Jisun” 以及 “Logout” LinkButton (Click 以檢視完整大小的影像)

Logout.aspx顯示

圖 19:Logout.aspx顯示「歡迎、) 」以及「登入」LinkButton (按兩下即可檢視大小完整的影像)

注意

我們鼓勵您自定義Logout.aspx頁面,以隱藏主版頁面的LoginContent ContentPlaceHolder (,就像我們在步驟 4) 中所做的Login.aspx一樣。 原因是 LoginStatus 控件所轉譯的 「Login」 LinkButton (「Hello, 排序」 底下的連結按鈕 ) 將使用者傳送至傳入 ReturnUrl 查詢字元串參數中目前 URL 的登入頁面。 簡單來說,如果註銷的使用者按兩下此LoginStatus的 「登入」LinkButton,然後登入,則會重新導向回Logout.aspx,這很容易混淆使用者。

摘要

在本教學課程中,我們從檢查窗體驗證工作流程開始,然後轉向在 ASP.NET 應用程式中實作窗體驗證。 窗體驗證是由 FormsAuthenticationModule 提供,其有兩個責任:根據窗體驗證票證識別使用者,以及將未經授權的使用者重新導向至登入頁面。

.NET Framework 的 FormsAuthentication 類別包含建立、檢查及移除窗體驗證票證的方法。 Request.IsAuthenticated 屬性和 User 物件提供額外的程式設計支援,以判斷是否已驗證要求,以及使用者身分識別的相關信息。 另外還有 LoginView、LoginStatus 和 LoginName Web 控件,可讓開發人員快速、無程式碼的方式執行許多常見的登入相關工作。 我們將在未來的教學課程中更詳細地檢查這些和其他登入相關的 Web 控件。

本教學課程提供窗體驗證的游標概觀。 我們並未檢查各種組態選項、查看無 Cookie 窗體驗證票證的運作方式,或探索 ASP.NET 如何保護窗體驗證票證的內容。

快樂的程序設計!

深入閱讀

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

本教學課程中包含的主題影片訓練

關於作者

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

特別感謝...

本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的潛在客戶檢閱者是本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者包括 Alicja Maziarz、John Suru 和 Teresa Murphy。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。