﻿# 簡介 ASP.NET 表單驗證 (FormsAuthentication) 的運作方式

我個人實在沒太多時間解釋整個 Forms Authentication 的運作機制，不過有些不錯的文章可以介紹給大家看。

![ASP.NET Forms Authentication](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/280f3843241ba46bb957-200625526-60a28176-29b2-4fe0-81f3-d1f4c07d8b84.png)

-   [An Overview of Forms Authentication (C#)](https://learn.microsoft.com/en-us/aspnet/web-forms/overview/older-versions-security/introduction/an-overview-of-forms-authentication-cs?WT.mc_id=DT-MVP-4015686)
    
    **看完這篇就幾乎全懂了！**
    
    這篇文章有中文翻譯: [表單驗證概觀 (C#)](https://learn.microsoft.com/zh-tw/aspnet/web-forms/overview/older-versions-security/introduction/an-overview-of-forms-authentication-cs?WT.mc_id=DT-MVP-4015686)
    
-   [Explained: Forms Authentication in ASP.NET 2.0](http://www.guidanceshare.com/wiki/Explained:_Forms_Authentication_in_ASP.NET_2.0)
    
-   [FormsAuthentication 類別(System.Web.Security)](https://learn.microsoft.com/zh-tw/dotnet/api/system.web.security.formsauthentication?view=netframework-4.8&WT.mc_id=DT-MVP-4015686)
    
-   [Accessing the ASP.NET FormsAuthentication "Timeout" Value](http://www.hanselman.com/blog/accessing-the-aspnet-formsauthentication-timeout-value)
    
    你要使用 Forms Authentication 可以用許多方式，可以用 ASP.NET 的 Login 控制項，也可以自訂表單做登入動作。而我今天會分享一些自訂表單登入的寫作心得。
    

### 常見應用情境

1.  最簡單的登入方式
    
    透過 [FormsAuthentication](https://learn.microsoft.com/zh-tw/dotnet/api/system.web.security.formsauthentication?view=netframework-4.8&WT.mc_id=DT-MVP-4015686) 類別中的一個 [RedirectFromLoginPage](https://learn.microsoft.com/zh-tw/dotnet/api/system.web.security.formsauthentication.redirectfromloginpage?view=netframework-4.8&WT.mc_id=DT-MVP-4015686) 靜態方法，這是最簡單、最無腦的一段程式碼：
    
    ```csharp
    string strUsername = "will";
    
    FormsAuthentication.RedirectFromLoginPage(strUsername, false);
    ```
    
    基本上，只要執行這段程式碼，就會自動回應一個名為 `.ASPXAUTH` 的 Cookie 給用戶端瀏覽器，使用者就會是「登入」狀態了。
    
2.  判斷使用者是否是「已登入」狀態
    
    ```csharp
    if(User.Identity.IsAuthenticated)
    {
        Response.Write("您現在是已登入狀態。");
    }
    ```
    
3.  取得登入者的帳號
    
    ```csharp
    string strLoginID = User.Identity.Name;
    ```
    
    > 使用這段程式碼要注意，如果使用者「尚未登入」的話 `User.Identity` 會回傳 `null` 回來，進而導致使用 `User.Identity.Name` 時發生 Exception !!
    
4.  自訂更細緻的登入過程
    
    雖然你可以很輕易的執行「登入」動作，但是預設來說 ASP.NET 2.0 的 Forms Authentication 是使用 Cookie 來做儲存登入資訊的動作，且預設的過期時間是 30 分鐘，如果希望設定成 Browser 全部關閉後會自動登出的話，就必須採用自訂 Forms Authentication Cookie 的方式，程式碼片段如下：
    
    ```csharp
    string userData = "ApplicationSpecific data for this user";
    
    string strUsername = "你想要存放在 User.Identy.Name 的值，通常是使用者帳號";
    
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
      strUsername,
      DateTime.Now,
      DateTime.Now.AddMinutes(30),
      isPersistent,
      userData,
      FormsAuthentication.FormsCookiePath);
    
    // Encrypt the ticket.
    string encTicket = FormsAuthentication.Encrypt(ticket);
    
    // Create the cookie.
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
    ```
    
    以上這段程式碼片段就是用來產生 Forms Authentication 的 Ticket，並將取得的 Ticket 編碼後設定儲存在使用者 Browser 的 Cookie 中。但其中有個參數叫做 "userData"，我也是研究了一下子才知道怎麼用他，想知道的人可以參考 [FormsAuthenticationTicket.UserData](https://learn.microsoft.com/zh-tw/dotnet/api/system.web.security.formsauthenticationticket.userdata?view=netframework-4.8&WT.mc_id=DT-MVP-4015686) 屬性。
    
    所以要取得登入時設定的 `userData` 的方式，可以參考以下程式片段：
    
    ```csharp
    FormsIdentity id = (FormsIdentity)User.Identity;
    FormsAuthenticationTicket ticket = id.Ticket;
    
    cookiePath.Text   = ticket.CookiePath;
    expireDate.Text   = ticket.Expiration.ToString();
    expired.Text      = ticket.Expired.ToString();
    isPersistent.Text = ticket.IsPersistent.ToString();
    issueDate.Text    = ticket.IssueDate.ToString();
    name.Text         = ticket.Name;
    userData.Text     = ticket.UserData;
    version.Text      = ticket.Version.ToString();
    ```
    
    > 但是有點要特別注意，就是您應該要限制儲存在 `UserData` 屬性中的資料量，因為 `UserData` 屬性所設定的資料最後會加密後儲存在 Cookie 中，雖然在 [RFC 2109 HTTP State Management Mechanism](http://www.ietf.org/rfc/rfc2109.html) 規範的 [6.3 Implementation Limits](https://www.ietf.org/rfc/rfc2109.html#section-6.3) 中有定義 User Agent (瀏覽器) 針對 Cookie 的最低儲存量，但是其 Cookie 的儲存量還是有其限制的，如果你設定超過了其限制，就有可能造成怎樣都無法登入成功的狀況！
    
5.  登出帳號
    
    最後，如果你要登出的話，可以用以下程式碼片段：
    
    ```csharp
    FormsAuthentication.SignOut();
    ```
    

### 關於 Web.config 的設定

如果 ASP.NET Forms Authentication 沒辦法順利執行，有可能是因為 `Web.config` 設定錯誤或因為較新版本的 .NET Framework 版本的預設值變更導致的，建議參考以下範例進行設定，這裡的重點在 `<system.web>` 底下的 `<authentication mode="Forms">` 設定！

```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  ...
  <system.web>
    
    ...
    
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" timeout="2880" />
    </authentication>
    
    ...
    
  </system.web>
  ...
</configuration>
```

### 相關連結

-   [ASP.NET 自訂角色的方式 (不用實做 Role Provider)](/post/2008/06/11/How-to-define-Roles-but-not-implementing-Role-Provider-in-ASPNET)
-   [如何在 ASP.NET Web API 2 專案中啟用表單驗證 (Forms Authentication)](/post/2016/09/22/Enable-FormsAuthentication-in-ASPNET-Web-API-2)
-   [解決 IE10 瀏覽器無法使用 ASP.NET 表單驗證登入的問題](/post/2012/11/17/IE10-unable-login-using-FormsAuthentication-in-ASPNET-40)
