2012年12月31日 星期一

Form驗證

什麼是 Forms 認證

Forms 認證是最廣泛應用的認證方式,尤其是應用在對外公開的網站,而不是僅內部人員使用的網站。

Forms 認證需要使用者提供密碼驗證,通常他們的資料來自於外部資料來源,如 Membership 資料庫,或是應用程式的組態檔中。 當使用者透過表單驗證成功之後,ASP.NET 會回應一個 cookie 給瀏灠器,作為驗證語彙基元(authentication token),用來表示這個認證過的使用者。 瀏灠器後續提出給網站的需求,都會同時送出這個 Token ,這樣子就不用每次要求都提供認證。 ASP.NET 則可以依這個 Token 驗證使用者的權限。

Froms 認證流程

Forms 認證的設定

如何啟用 Forms 認證

要啟用 Forms 認證,只要將 authentication 組態項目的 mode 屬性設定為 Forms 即可。

<authentication mode="Forms">
      <forms loginUrl="~/login.aspx" 
             defaultUrl="~/default.aspx" 
             cookieless="AutoDetect" 
             protection="Validation"/>
</authentication>

Forms 認證的相關屬性設定

底下是 Form 標記的屬性範例

<authentication mode="Forms">
    <forms 
        loginUrl="~/login.aspx" 
        defaultUrl="~/index.aspx" 
        protection="All"
        timeout="30"
        requireSSL="false"
        cookieless="UseDeviceProfile"
        enableCrossAppRedirects="false"/>
</authentication>

下表是 forms 屬性的簡要說明:

  • loginUrl:指定找不到有效的驗證 Cookie 時,將要求重新導向以進行登入的 URL。( 預設值為 login.aspx )
  • defaultUrl:驗證後重新導向的預設 URL。
  • name:指定用來驗證的 HTTP Cookie。(預設值為 ".ASPXAUTH")
    如果在單一伺服器執行多個應用程式,而且每個應用程式都需要唯一的 Cookie,就必須在每個應用程式的 Web.config 檔中設定 Cookie 名稱。
  • path:應用程式發出 Cookie 的路徑。預設值為 (/)。
  • requireSSL:指定傳輸驗證 Cookie 是否需要 SSL 連接。(預設值為 False)
  • slidingExpiration:指定是否啟用滑動期限。(預設為 True)
  • timeout:指定 Cookie 過期的時間,以整數分鐘為單位。(預設為 30 分鐘)
  • ticketCompatibilityMode:指定是否要使用 UTC 或當地時間做為表單驗證的票證到期日。(預設值是 Framework20)
    • Framework20:指定使用當地時間儲存票證到期日。
    • Framework40:指定使用 UTC 儲存票證到期日。
  • protection:指定 Cookie 要使用的加密類型。 (預設值為 All)
    • All:指定應用程式同時使用資料驗證和加密來協助保護 Cookie。
    • Encryption:指定應用程式使用 3DES 加密 Cookie,但是不對 Cookie 執行資料驗證。
    • None:不加密也不驗證。
    • Validation:指定驗證配置驗證加密且轉換時未被改變的 Cookie 。
  • cookieless 屬性:定義 Token 是否使用 Cookie 存放。(預設值為 UseDeviceProfile)
    • UseCookies:不論用戶端支援為何,都使用 cookie 表單驗證,也就是永遠傳送 Cookie 給用戶端。
    • UseUri:不論用戶端支援為何,都使用無 cookie 表單驗證,也就是將 Token 存放於 URL。
    • AutoDetect:若裝置設定檔支援 Cookie 就使用 cookie 表單驗證。不過 ASP.NET 仍會測試用戶端是法支援 cookie ,若支援,則使用 cookie 表單驗證;若不支援,則使用無 cookie 表單驗證。
    • UseDeviceProfile:只要瀏覽器有支援 Cookie ,即便用戶端停掉 Cookie,都會傳送 Cookie。但是,若用戶端停掉 Cookie ,則表單驗證無法順利執行。

更詳情的說明,請參考 MSDN: 表單驗證的設定項目

FormsAuthenticationModule

FormsAuthenticationModule 是 ASP.NET 提供的表單驗證機制, 在 machiene-level 的 Web.config 檔中,預設會包含加入這個機制。 如果要啟用只需要如下的設定即可:

<authentication mode="Forms" />

The FormsAuthenticationModule class constructs a GenericPrincipal object and stores it in the HTTP context. The GenericPrincipal object holds a reference to a FormsIdentity instance that represents the currently authenticated user. You should allow forms authentication to manage these tasks for you. If your applications have specific requirements, such as setting the User property to a custom class that implements the IPrincipal interface, your application should handle the PostAuthenticate event. The PostAuthenticate event occurs after the FormsAuthenticationModule has verified the forms authentication cookie and created the GenericPrincipal and FormsIdentity objects. Within this code, you can construct a custom IPrincipal object that wraps the FormsIdentity object, and then store it in the HttpContext.

Note If you do this, you will also need to set the IPrincipal reference on the Thread.CurrentPrincipal property to ensure that the HttpContext object and the thread point to the same authentication information.

FormsAuthentication 類別

The FormsAuthentication class creates the authentication cookie automatically when the FormsAuthentication.SetAuthCookie or FormsAuthentication.RedirectFromLoginPage methods are called.

下表是 FormsAuthentication 類別提供的方法和屬性,可以用來檢驗應用程式中使用者的驗證資訊。

屬性:

  • FormsCookieName :取得用來存放表單驗證票證的 Cookie 名稱。
  • FormsCookiePath :取得表單驗證 Cookie 的路徑。
  • RequireSSL :指出表單驗證 Cookie 是否需要 SSL 才能傳回至伺服器。
  • Timeout :取得驗證票證逾時前的時間量。
  • SlidingExpiration :取得一個指示是否啟用變動到期的值。

方法:

  • Authenticate :根據存放於應用程式組態檔中的認證,驗證使用者名稱和密碼。
  • Decrypt :根據傳遞至方法的已加密表單驗證票證,建立 FormsAuthenticationTicket 物件。
  • Encrypt :建立包含已加密表單驗證票證 (適用於 HTTP Cookie 中) 的字串。
  • GetAuthCookie :建立指定使用者名稱的驗證 Cookie。
  • GetRedirectUrl :取得被導到登入頁前的原本 URL。
  • RedirectFromLoginPage :將已驗證的使用者重新導向回到原來要求的 URL 或預設 URL。
    這個方法的第二個參數,若設成 true ,表示建立持久性 Cookie (跨瀏覽器工作階段儲存的 Cookie), 若設成 false,表示建立暫時性 Cookie ,也就是只存放在記憶體之中,瀏覽器關掉就自動清除。 這個功能相當於 Login 控制項中,「記憶密碼供下次使用」的設定。
  • RedirectToLoginPage :將瀏覽器重新導向至登入頁。
  • SetAuthCookie :為所提供的使用者名稱建立驗證票證,並將該票證加入至回應的 Cookie 集合,或加入至 URL (若使用的是 Cookieless 驗證)。
  • SignOut :從瀏覽器移除表單驗證票證。

實作 Forms 認證

當使用 Windows 認證,瀏灠器會自已產生一個對話視窗,要求使用者輸入帳號密碼,所以不用自行設計登入頁面。 若是採用 Forms 認證,則必須自行設計登入頁面。

要使用 Forms 認證可以有多種方法,例如:搭配 ASP.NET 的 Login 控制項,或者使用自訂的表單。

使用 Login 控制項進行驗證

使用 Login 控制項進行驗證,請參考前一節中的介紹。

使用自訂表單進行驗證

使用 Login 控制項,可以省下很多事情,但有時候遇到需要客制化時,還是得自已來。 底下示範,如何使用自訂驗證表單驗證使用者的技巧。 依照使用者資料來源的不同,方法有所不同:

驗證 Web.config 中的使用者資料

假設我們在 web.config 設定了以下幾組使用者資料

<authentication mode="Forms">
  <forms loginUrl="login.aspx" protection="Encryption" timeout="30" >
    <credentials passwordFormat="SHA1" >
        <user name="Eric" password="7110EDA4D09E062AA5E4A390B0A572AC0D2C0220"/>   <!--1234-->
        <user name="Sam"  password="81FE8BFE87576C3ECB22426F8E57847382917ACF"/>   <!--abcd-->
    </credentials>
  </forms>
</authentication>

如果是要驗證上述組態檔中的 credentials 區段中的使用者,可以用以下方法:

//驗證 Web.config 中的使用者資料
if (FormsAuthentication.Authenticate(UserName, Password)) 
    myMessage.Show(this, "Authenticate Successed");
else
    myMessage.Show(this, "Authenticate Failed");

這個方法注意事項:

驗證 Membership Provider 中的使用者資料

如果是驗證 ASP.NET 的 Membership 的使用者,可以使用 Membership.ValidateUser 方法驗證:

//驗證 Membership 中的使用者資料
if (Membership.ValidateUser(UserName, Password))
    myMessage.Show(this, "Authenticate Successed");
else
    myMessage.Show(this, "Authenticate Failed");

驗證自訂的使用者資料

如果是驗證自訂的使用者,則自行撰寫驗證方法:

//驗證 自訂 的使用者資料 (Authentication 為自訂類別)
string sUserID;
if (MyValidate(UserName, Password, out sUserID))
{
    string ReturnUrl = "";
    if (Request.QueryString["ReturnUrl"] != null)
        //取得原來要求的 URL
        ReturnUrl = Request.QueryString["ReturnUrl"].ToString();    ///TestAuthenticationWebSite/UserHome.aspx
    else
        //取得 web.config 中預設的 URL
        ReturnUrl = FormsAuthentication.DefaultUrl;     ///TestAuthenticationWebSite/default.aspx

    //不過, 不需要以上那麼麻煩, 只要使用以下方法, 就可以自動判斷有沒有原來要求的 URL
    //因為 FormsAuthentication 會自動判斷網址列中的所有參數,是否含有網址的參數值。
    //若有, 就會導向原來要求的 URL; 若沒有就會導向 預設的 URL
    FormsAuthentication.RedirectFromLoginPage(UserName, true);
}

變更狀態成為已登入

不管使用何種驗證方式,當驗證過關後,你必須實作一段程式碼來記住使用者已經登入的狀態,例如使用 Session 來記錄,不過底下我們直接利用 FormsAuthentication 物件來設計,它會使用 Cookie 來記錄。 你只要叫用它的 SetAuthCookieRedirectFromLoginPage 方法,它就會變更 User.Identity.IsAuthenticated 值,並且將登入記錄送到用戶端 Browser 的 Cookie 中。

RedirectFromLoginPage 這個方法的第二個參數,若設定為 true ,則 Cookie 資訊會儲存在 Client 端的磁碟中,有效期限預設為 30 分鐘; 若設定為 false ,則 Cookie 資訊只會存放在 Client 端瀏灠器的記憶體中,所以則當使用者關閉瀏灠器後, Cookie 就會失效。

if (Authentication.Validate(UserName, Password, out sUserID))
{
    bool bPersistentCookie = true;
    FormsAuthentication.RedirectFromLoginPage(UserName, bPersistentCookie);
}

當叫 FormsAuthentication.RedirectFromLoginPage 方法時,它會自動建立一個 forms-authentication ticket ,並將該 ticket 編碼後儲存在使用者 Browser 的 Cookie 中。 如果我們要儲存特定的使用者資訊,可以自己建立 FormsAuthenticationTicket,然後寫入 Authentication Cookie。

int version = 1;
        string name = account;
        DateTime issueDate = System.DateTime.Now;
        DateTime expiration = System.DateTime.Now.AddMinutes(2);  //1分
        bool isPersistent = rememberme.Checked;
        string userData = password;
        string cookiePath = FormsAuthentication.FormsCookiePath;

        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
            version,                        /*票證的版本號碼*/
            name,                           /*使用者名稱*/
            issueDate,                      /*核發時間*/
            expiration,                     /*到期時間*/
            isPersistent,                   /*是否將票證資訊存放到硬碟的 Cookie 中*/
            userData,                       /*使用者特定資料*/
            cookiePath                      /*存放於 Cookie 中時的路徑*/
            );

        // 對 ticket 進行編碼
        string encTicket = FormsAuthentication.Encrypt(ticket);

        // 將該票證加入至回應的 Cookie 集合
        FormsAuthentication.SetAuthCookie(name, isPersistent);

當叫 FormsAuthentication.SetAuthCookie 方法時,該 Cookie 的有效時限預設為 30 分,可由 web.config 底下屬性設定變更。

<system.web>
  <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" defaultUrl="~/SysAdmin/index.aspx" timeout="30" name="VitoWebAuth" />
           ...

若要由程式決定 Cookie 到期時間,則自行輸出 Cookie 即可。

// 對 ticket 進行編碼
        string encTicket = FormsAuthentication.Encrypt(ticket);

        // 手動方式將該票證加入至回應的 Cookie 集合
        HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
        cookie.Expires = System.DateTime.Now.AddDays(3);  //3天
        Response.AppendCookie(cookie);

重新導向

上面的 RedirectFromLoginPage 靜態方法,除了會記住使用者登入記錄,另外會將已驗證的使用者重新導向回到原來要求的 URL 或預設 URL。 它會自動判斷網址列中是否帶有 ReturnUrl 參數,若有就會導向該網址,若沒有就會導向 web.config 中預設的 URL 。 如果你不想導向 ReturnUrl 或者預設的 URL ,你只要使用 SetAuthCookie ,再 Redirect 你要的網址就好。

// 對 ticket 進行編碼
    string encTicket = FormsAuthentication.Encrypt(ticket);

    // 將該票證加入至回應的 Cookie 集合
    FormsAuthentication.SetAuthCookie(name, isPersistent);

    // 因為採用 SetAuthCookie 回應 authentication ticket , 所以必須自行做重新導向.
    // 若使用 FormsAuthentication.RedirectFromLoginPage 則不用.
    string ReturnPath = Request.QueryString["ReturnUrl"];
    if (ReturnPath != null)
    {
        Response.Redirect(ReturnPath);
    }
    else
    {
        Response.Redirect(FormsAuthentication.DefaultUrl);
    }

判斷使用者是否已經登入

當我們透過 RedirectFromLoginPageSetAuthCookie 或手動回應一個 authentication ticket 型態的 cookie 後, 等到下一次的 request 這個 cookie 會自動被帶回 server 端,這時候在 sever 端可以直接使用以下方法,判斷是否已經驗證。

//使用 User.Identity.IsAuthenticated 判斷
if(User.Identity.IsAuthenticated)
{
    Response.Write("您現在是已登入狀態。");
}
//或者使用 Page.Request.IsAuthenticated 也可以
if(Page.Request.IsAuthenticated)
{
    Response.Write("您現在是已登入狀態。");
} 

取得登入的帳號

登入後要取得登入的帳號,可以用以下程式碼片段:

string sUserID = User.Identity.Name;

登出

若要執行登出,可以用以下程式碼片段:

// 清除 cookie
    FormsAuthentication.SignOut();

    // 導至登入頁 (依需求)
    FormsAuthentication.RedirectToLoginPage()

ticket expiration v.s. cookie expiration

Cookie 小常識

沒有留言:

張貼留言