ASP.NET 2.0 表單身份認證心得筆記

在網網相連中,看到一篇「Explained: Forms Authentication in ASP.NET 2.0」覺得寫的很好,雖然第一行的黃色字有點…,還是自己寫點筆記留個記錄。

ASP.NET身分驗證流程有二:IIS --> ASP.NET。

第一,一般對外網站在IIS部分都會使用匿名身份驗證(anonymous authentication),然後將使用傳遞給ASP.NET。
第二,ASP.NET執行自己的身份驗證。我們可以在 web.config 設定檔裡由authentication設定 mode 屬性來決定驗證的模式。

FileAuthorizationModule 類別
<authentication mode="Forms" />

ASP.NET 表單身分驗證

在ASP.NET裡,我們一般都是使用表單來驗證使用者。我們可以在 web.config 設定檔裡設定表單身分驗證預設屬性值。

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="Login.aspx"
           protection="All"
           timeout="30"
           name=".ASPXAUTH" 
           path="/"
           requireSSL="false"
           slidingExpiration="true"
           defaultUrl="default.aspx"
           cookieless="UseDeviceProfile"
           enableCrossAppRedirects="false" />
  </authentication>
</system.web>

forms 預設屬性值

  • loginUrl
    指定指向您的應用程式自訂的登入頁(Login.aspx)。
  • protection
    保護所有設置,身份驗證ticket,會使用machineKey元素來進行加密。
  • timeout
    用於指定的表單身份驗證Session的有效時間。預設值為 30 分鐘。
  • name
    name和path設置為應用程式的設定檔中定義的值。
  • requireSSL
    此設定是指該 cookie 可以傳輸不是 SSL 加密的身份驗證。如果您擔心Session劫持,則應考慮為true。
  • slidingExpiration
    執行滑動的Session生存期設定為true。只要在網站上的使用者仍處於活動狀態,定期重新設定timeout的Session。
  • defaultUrl
    Default.aspx 預設首頁。
  • cookieless
    無cookie來指定應用程式使用的所有流覽器都支援 cookie 的 cookie 設置為UseDeviceProfile。如果流覽器不支援 cookie 訪問該網站,然後 forms 身份驗證套裝程式 URL 上的身份驗證票。
  • enableCrossAppRedirects
    指示該表單身份驗證不支援自動處理的查詢字串或表單 POST 的一部分的應用程式之間傳遞的ticket。

通過身份驗證認證後,我們還必須給適當的權限。

授權配置

UrlAuthorizationModule 類別 驗證使用者確實擁有權限,可以存取所要求的 URL。

透過 authorization 元素來設定UrlAuthorizationModule

<system.web>
  <authorization>
    <deny users="?" />
  </authorization>
</system.web>

此使此設定後,未經身份驗證的所有使用者都被拒絕訪問您網站的任何頁面。而且會將使用者重新導向登入頁(loginUrl屬性)。

表單身份驗證流程


  1. 使用者請求 Default.aspx 從您的IIS應用程式。IIS 允許請求,因為 IIS 啟用匿名訪問。ASP.NET確認授權的元素包括<deny users="?" />。
  2. 伺服器將尋找為身份驗證 cookie。如果未能找到身份驗證 cookie,則將使用者重導向到設定的登入頁 (Login.aspx)。起始頁的資訊放在RETURNURL作為Key的查詢字串。伺服器的 HTTP 答覆如下所示:
    302 Found Location:
    http://localhost/FormsAuthTest/login.aspx?RETURNURL=%2fFormAuthTest%2fDefault.aspx
  3. 瀏覽器請求 Login.aspx 頁面,並在查詢字串中包含RETURNURL參數。
  4. 伺服器將返回登入頁面和 200 OK HTTP 狀態碼。
  5. 使用者在登入頁面上輸入帳號密碼,返回到伺服器。
  6. 伺服器驗證使用者帳號密碼,從如 SQL Server 資料庫或AD。在登入頁面中的撰寫程式碼,建立一個包含表單的身份驗證ticket為Session設定的 cookie。
    我們可以使用成員資格(Membership)系統:

    If (Membership.ValidateUser(userName.Text, password.Text)) Then
    
        If (Request.QueryString["ReturnUrl"] IsNot Nothing) Then
            FormsAuthentication.RedirectFromLoginPage(userName.Text, false);
        Else
            ' 設定Cookie
            FormsAuthentication.SetAuthCookie(userName.Text, false);
        End If
    
    Else
        Response.Write("Invalid UserID and Password")
    End If
    
  7. 身份驗證的使用者中,伺服器將瀏覽器重新導向到原始 URL 查詢字串中指定的RETURNURL參數。伺服器的 HTTP 答覆如下所示:
    302 Found Location:
    http://localhost/TestSample/default.aspx
  8. 重新導向瀏覽器再次請求 Default.aspx 頁。此請求包含該表單的身份驗證 cookie。
  9. FormsAuthenticationModule類別檢測表單身份驗證 cookie,並對使用者進行身份驗證。身份驗證成功之後,FormsAuthenticationModule類別寫入HttpContext物件,有關身份驗證的使用者的資訊將被公開為屬性。
  10. 由於伺服器已驗證身份驗證 cookie,它授予存取權限,並返回 Default.aspx 頁。

FormsAuthenticationModule

在ASP.NET 2.0的本機 Web.config 設定檔(指的是 C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG 之下的 Web.config),定義了一組httpModules。裡面包含了身分驗證模組的數量。
<httpModules>
            <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
            <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
            <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule"/>
            <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/>
            <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule"/>
            <add name="RoleManager" type="System.Web.Security.RoleManagerModule"/>
            <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"/>
            <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule"/>
            <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule"/>
            <add name="Profile" type="System.Web.Profile.ProfileModule"/>
            <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
            <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
        </httpModules>
只有一個身分驗證模組會用於每個請求。通常我們會在網站中的 Webconfig 裡指定身份驗證模式。
<authentication mode="Forms" />
FormsAuthenticationModule類別建構一個GenericPrincipal物件,並將其儲存在 HTTP content。GenericPrincipal物件包含表示目前已通過身份驗證的使用者的FormsIdentity實例的參考。您應該允許表單身份驗證為你管理這些任務。如果你的應用程式有特別的要求,如使用者屬性設定在自訂的類別中實現IPrincipal介面,你的應用程式應該處理PostAuthenticate事件。FormsAuthenticationModule已驗證表單身份驗證的 cookie 並建立的GenericPrincipal和FormsIdentity物件之後,將觸發PostAuthenticate事件。在此程式碼中,可以建構包含的FormsIdentity物件及自訂的IPrincipal物件,然後將它儲存在HttpContext。 請注意:如果你這樣做,您還需要設定IPrincipal參考Thread.CurrentPrincipal屬性,以確保HttpContext物件和執行緒指向相同的身份驗證資訊。

表單身份驗證 Cookie

FormsAuthentication.SetAuthCookie或FormsAuthentication.RedirectFromLoginPage的方法被呼叫時,FormsAuthentication類別將自動建立身份驗證 cookie。

在典型的表單身份驗證 cookie 中包含以下屬性:

  • Name:此屬性指定 cookie 的名稱。
  • Value:此屬性指定 cookie 的值。

在典型的表單的身份驗證 cookie中,Value包含加密和簽名的FormsAuthenticationTicket物件字串表示形式。Cookie 包含以下屬性:

  1. Expires:此屬性指定 cookie 的過期日期和時間。
  2. Domain:此屬性指定 cookie 相關聯的網域。預設值為空( null or Nothing ).
  3. HasKeys:此屬性指示是否該 cookie 有次項目。
  4. HttpOnly:此屬性指定用戶端指令碼(client script)是否可以訪問 cookie。在 ASP.NET 2.0,此值始終設定為true。請注意不支援HttpOnly 屬性的 web 流覽器忽略該 cookie,或忽略該屬性,這意味著該Session仍有跨網站腳本攻擊可能。
  5. Path:此屬性指定 cookie 的虛擬路徑。預設值是"/",指定根目錄。
  6. Secure:此屬性指定是否僅應通過 HTTPS 連接傳輸 cookie。安全屬性應設置為true,這樣,cookie 受 SSL 加密。
  7. Version:此屬性指定 cookie 的版本號。

建立表單的身份驗證 Cookie

FormsAuthentication類別,如下所示建立表單的身份驗證 cookie。一旦使用者進行驗證,FormsAuthentication類別內部建立一個FormsAuthenticationTicket物件指定 cookie 的各項屬性。

' FAT(版本,名稱,核發日期,到期日期,永續性,使用者特定資料,目錄路徑)
' 使用者特定資料:userData 參數不能為 Nothing
Dim ticket As New FormsAuthenticationTicket(1,
                                            "userName",
                                            DateTime.Now,
                                            DateTime.Now.AddMinutes(30),
                                            False,
                                            String.Empty,
                                            FormsAuthentication.FormsCookiePath)

如果表單元素的protection屬性設定為All或Encryption,表單身份驗證使用的加密方法加密和簽名表單的身份驗證ticket。

Dim encryptedTicket As String = FormsAuthentication.Encrypt(ticket)

下面的內容顯示時protection屬性設定為All的過程:

  • 建立序列化的表單身份驗證ticket。建立的ticket以byte陣列表示。
  • 簽錄表單身份驗證ticket。
  • 加密表單身份驗證ticket。
  • 根據需要建立 HTTP cookie 或查詢字串。
    Dim authcooket As New HttpCookie(FormsAuthentication.FormsCookieName,
                                     encryptedTicket)
    
  • 設定表單的身份驗證 cookie 為Secure。
    authcooket.Secure = True
  • 設定HttpOnly。
  • 設定適當的 cookie 屬性。
  • 加入該 cookie 到 cookie集合。
    Response.Cookies.Add(authcooket)

每次接收到後續身份驗證請求後,FormsAuthenticationModule類別檢查身份驗證 cookie 的身份驗證ticket, 對它進行解密, 計算雜湊值中,和將有助於確保 cookie 不被篡改的 MAC (message authentication code) 值進行比較。最後,驗證的表單身份驗證ticket內所記載的過期時間。

請注意,因為此日期可以很容易偽造,ASP.NET不依賴 cookie 的過期日期。

角色授權

.NET Framework 2.0包含角色管理 API 使您能夠建立和刪除角色,並增加使用者和從角色中刪除使用者。

  • SQL Server:這是預設的Provider,並將角色資訊存儲在 SQL Server 資料庫中。
  • 授權管理器 (AzMan)。此Provider使用 AzMan 策略存儲在 XML 檔中,在AD中,或在AD中應用模式 (ADAM, Active Directory Application Mode) 作為其角色儲存。它通常用於 intranet 或外部網路 Windows 身份驗證和 Active Directory 用於進行身份驗證。

Cookieless 的表單身份驗證

表單元素的屬性控制此 Cookieless 功能。

  • UseCookies:此值強制的FormsAuthenticationModule類別使用 cookie 傳輸的身份驗證ticket。
  • UseUri:此值指示FormsAuthenticationModule類別重寫該 URL 傳遞身份驗ticket。
  • UseDeviceProfile:此值指示依看瀏覽器功能來決定,如果瀏覽器支援 cookie,就使用 cookie ;否則,該 URL 會被重寫。
  • AutoDetect:此值指示FormsAuthenticationModule類別檢測瀏覽器是否支援通過動態檢測機制的 cookie。如果檢測邏輯表示不支援 cookie,然後該 URL 就會被重寫。

如果您的應用程式設定為使用 cookieless 的表單身份驗證,並且正在使用FormsAuthentication.RedirectFromLoginPage方法,FormsAuthenticationModule類別會自動設定表單身份驗證ticket在 URL 中,例如,

http://localhost/CookielessFormsAuthTest/(F(-k9DcsrIY4CAW81Rbju8KRnJ5o_gOQe0I1E_jNJLYm74izyOJK8GWdfoebgePJTEws0Pci7fHgTOUFTJe9jvgA2))/Test.aspx

在 url 括弧中部分包含的 cookie 會包含的資料。在請求處理過程中,這些資料會被刪除。

成員資格和登入控制項

會員服務provider的憑證儲存和管理應用程式的使用者。它還提供了 API,當使用表單身份驗證時,簡化了驗證使用者憑證的工作。成員資格功能是提供provider model的基礎上建立的。此 model 允許實施和配置不同的Provider,指向不同的使用者儲存區。

  • Active Directory membership provider
  • SQL Server membership provider

您還可以增加自訂的使用者儲存的支援。建立MembershipProvider抽象基礎類別繼承的自訂provider。

ASP.NET 登入控制項自動使用成員資格和表單身份驗證,並將提示使用者輸入憑證、 驗證使用者、 恢復或更換密碼,等所需的邏輯封裝。

Web Farm

在 Web Farm中,不能保證哪台伺服器連續處理請求。如果一台伺服器上通過身份驗證的使用者和下一個請求轉到另一個伺服器,身份驗證ticket將失敗的驗證,並要求使用者重新進行身份驗證。

MachineKey元素中的validationKey和decryptionKey屬性用於雜湊演算法和加密的表單身份驗證ticket。這些屬性的預設值是AutoGenerate.IsolateApps。每個應用程式會動產生Key,而且每台伺服器上不同。為解決這一問題,validationKey和decryptionKey的值必須在 Web Farm中的所有電腦上的相同。

9 則留言:

  1. Bruces您好:
    小弟公司最近正在開發內部公司系統,使用的是MVC4,目前對於使用者驗證這塊有點苦惱,小弟我目前是用CustomMembershipProvider+CustomRoleProvider+Form Authentication去完成驗證,但是公司主管提到需要共用一個登入口(protocol)來管理多個內部系統,拜讀您的書以後我還是不知道怎麼辦,不知道您能不能給小弟一點建議或方向,甚至是關鍵字也好,另外想請問登入的機制做成webApi在MVC4可行嗎,非常感謝>"<

    回覆刪除
  2. 多個“內部系統”…走“Windows認證”會不會比較好。(當然要有AD架構)你開個Intranet專案,根目錄下會有一個ReadMe.txt會有說明。

    另外可以參考一下SSO(Single Sign-On)的作法,關鍵字的話:「SSO、ASP.NET MVC SSO、MVC Single Sign-On」。

    Web API用在登入,有其他更進階做法,看看我有沒有時間寫成文章來說明。

    回覆刪除
    回覆
    1. 感謝您的建議,不過公司目前是以內部系統當磨刀的例子(在導入MVC4開發方式),所以應該還是會走Form Authentication的路線去嘗試,期待K大的WEB API登入教學文章>"<

      刪除
    2. MVC與用什麼登入方式無關,有AD架構的內部網站,本來就“合適”使用Windows認證,對外網站本來就“合適”使用Form認證,不然就是使用SSO,再不然可考慮用Windows Azure Active Directory(http://www.windowsazure.com/en-us/home/features/identity/)的方式來整合。

      導入MVC還是一步一步來,除非有熟手幫忙,不然實在不建議一開始就搞那麼複雜。

      刪除
    3. 不好意思我沒有說清楚,因為公司希望之後的對外專案可以使用MVC的方式做開發,所以才會把內部系統的需求比照對外網站的方式做開發,像是驗證登入部分當初才會沒考慮使用Windows認證,畢竟公司開發上沒有先例,很多東西都需要測試,不然也不敢接相關案子,不過也正如您所說的,這部分對新手來說實在有些複雜,我會把您提供的意見跟公司主管討論討論,再次感謝^^

      刪除
  3. MVC開發的優點就是“彈性”,“關注點分離”也是讓我們可以只關注一個點,實作一個點。

    多網站的登入,也不一定要由ASP.NET MVC(或ASP.NET Web API)來實作,例如,你可以參考ASP.NET Web Tools 2012.2更新後的“Facebook範本”做法,這種整合“社交媒體”來登入也是時下很流行且使用者接受度很高的方式,也是很不錯的方式。

    回覆刪除
  4. 你好, 想請問一下
    我架設了兩個website A(www.sam.com) 跟 website B(www.sam.com:8000), 用同一個domain, 不同的port number

    在website A 有login page , 用了Form Auth, 寫了cookie(.SAMCookie),
    然後我再到website B index page, 用fiddler 看到發出的request同樣有website A cookie(.SAMCookie) ,而value 也一樣的
    但Website B 就不能通過auth , 請問有甚麼地方有問題呢?

    兩個website 的web.config 也是設了以下的












    回覆刪除
    回覆
    1. cookie 不能跟 Domain 哦,不然沒有安全性可言了...

      刪除
    2. 為何, 可否說明

      如果設 CORS 能達成嗎? website b get website a cookie
      我在fiddler 看到website b 有 website a 的cookie , 但在Code behind request.cookie 就拿不到, 為何呢?

      刪除

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。