﻿# 如何在 ASP.NET Web API 2 專案中啟用表單驗證 (Forms Authentication)

使用 Visual Studio 2015 開發 ASP.NET Web API 2 專案時，有好幾種專案範本可以選擇，你可以從「空白」專案範本開始，也可以從「MVC」專案範本開始 (記得勾選 Web API 核心參考)，也可以直接從「Web API」專案範本開始，由於專案範本通常夾雜大量的範例程式，很多時候我們並無法在第一時間得知每段程式碼與設定檔的細節，以至於在後續開發的過程發生許多問題。本篇文章將帶大家了解如何成功在 ASP.NET Web API 2 專案正確啟用 ASP.NET 的表單驗證機制 (FormsAuthenticaion)。

> 針對不同的專案範本，我特別建立了一個可比較的 Git 專案，你只要先複製[這個 GitHub 專案](https://github.com/doggy8088/WebApi2ProjectTemplateCompare)回來，就可以比對不同專案範本之間的檔案差異之處。

這邊我寫了一個非常簡單的表單驗證程式碼，程式碼如下：

先建立一個 **LoginViewModel**

> using System.ComponentModel.DataAnnotations;
> 
> namespace *YourNamespace*.Models  
> {  
>     public class UserLoginViewModel  
>     {  
>         \[Required\]  
>         \[StringLength(20)\]  
>         public string Username { get; set; }  
>         \[Required\]  
>         public string Password { get; set; }  
>     }  
> }

再建立一個 **UsersController**

> using *YourNamespace*.Models;  
> using System.Net;  
> using System.Web.Http;  
> using System.Web.Security;
> 
> namespace *YourNamespace*.Controllers  
> {  
>     \[RoutePrefix("user")\]  
>     public class UsersController : ApiController  
>     {  
>         \[Route("login")\]  
>         public IHttpActionResult PostLogin(UserLoginViewModel user)  
>         {  
>             if (!ModelState.IsValid)  
>             {  
>                 return BadRequest();  
>             }
> 
>             if (user.Username == "admin" && user.Password == "123")  
>             {  
>                 FormsAuthentication.RedirectFromLoginPage(user.Username, false);  
>                 return StatusCode(HttpStatusCode.NoContent);  
>             }  
>             else  
>             {  
>                 return StatusCode(HttpStatusCode.Unauthorized);  
>             }  
>         }
> 
>         \[Route("logout")\]  
>         \[HttpGet\]\[HttpPost\]  
>         public void Logout()  
>         {  
>             FormsAuthentication.SignOut();  
>         }
> 
>         \[Authorize\]  
>         \[Route("profile")\]  
>         public IHttpActionResult GetProfile()  
>         {  
>             return Ok(User.Identity.Name);  
>         }  
>     }  
> }

基本上，上述這兩個檔案就可以完成最基本的登入 (PostLogin)、登出 (Logout) 等基本身分驗證功能，搭配 **\[Authorize\]** 動作過濾器 (Action Filter) 還可以對 取得登入者名稱 (GetProfile) 功能進行基本授權驗證，因此這是一份完成的表單驗證實作，非常容易上手。

以下我將介紹在不同的專案範本下，才能讓這段程式碼正常運作：

**1\. 使用「空白」專案範本開始，外加 Web API 核心參考 (如下圖示)**

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/347d53a5c1df187495fd-image_2940.png)

這部分只要在專案根目錄下的 Web.config 加入 **<authentication mode="Forms"></authentication>** 即可：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/2525af2e8b57a0b03702-image_2941.png)

**2\. 使用「MVC」專案範本開始，外加 Web API 核心參考 (如下圖示)**

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/8a12087051944bbd12d5-image_2942.png)

在 MVC 專案範本下，預設會停用所有表單驗證功能，如果要啟用表單驗證的話，則要修改 Web.config 兩個地方：

1.  調整 <authentication> 的 mode 屬性為 Forms ( 原本預設值為 None )
2.  刪除 <remove name="FormsAuthentication" /> 設定 (原本這行會停用所有表單驗證的功能)

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/104d1b7a60e82cf42c5b-image_2943.png)

由於 ASP.NET MVC 專案預設移除了 FormsAuthentication 模組，如果你沒有意識到這個模組預設被移除的話，會導致你的 Code 怎樣寫都能執行，登入也能跑，也不會發生例外，但登入者會永遠是「尚未登入」的鬼打牆狀態！

**3\. 使用「Web API」專案範本開始，外加 Web API 核心參考 (如下圖示)**

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/0fe7dc910c75a80caca2-image_2944.png)

在 Web API 專案範本下，預設也會停用所有表單驗證功能，但要改的地方又比 MVC 專案多一些，地雷實在很多啊！

如果要啟用表單驗證的話，除了要修改 **Web.config** 兩個地方 (跟 MVC 專案範本一樣)，還要修改 **App\_Start\\WebApiConfig.cs** 檔案：

修改 Web.config 兩個地方：

1.  調整 <authentication> 的 mode 屬性為 Forms ( 原本預設值為 None )
2.  刪除 <remove name="FormsAuthentication" /> 設定 (原本這行會停用所有表單驗證的功能)

[![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/af209d5b641126da6e94-image_thumb_220.png "image")](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/0bb88263e6ac98d5a633-image_2945.png)

修改 **App\_Start\\WebApiConfig.cs** 兩行程式碼，將其註解起來：

-   // config.SuppressDefaultHostAuthentication();
-   // config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/37e26e6369c9eadd2fbd-image_2946.png)

預設的 ASP.NET Web API 2 專案範本中，由於被設定成僅使用 bearer 權杖驗證，因此這兩行必須註解起來，表單驗證才能正常執行。

不過有一點要注意，如果你將這兩行註解起來的話，ASP.NET Web API 就不能走專案範本預設的 OAuth 驗證，專案內的 AccountController 也將無法正常使用！

本文所有程式碼，包含所有修改程式碼的版本紀錄，都已經整理到 [https://github.com/doggy8088/WebApi2FormsAuth](https://github.com/doggy8088/WebApi2FormsAuth "https://github.com/doggy8088/WebApi2FormsAuth") 這個專案中，大家可以先 git clone 回來，再透過 Git 工具 (TortoiseGit, SourceTree, … ) 即可查看每一個版本的變化歷程。

**相關連結**

-   [Secure a Web API with Individual Accounts and Local Login in ASP.NET Web API 2.2](http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api)
-   [Using cookie authentication middleware with Web API and 401 response codes](https://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/)
-   [Host authentication and Web API with OWIN and active vs. passive authentication middleware](https://brockallen.com/2013/10/27/host-authentication-and-web-api-with-owin-and-active-vs-passive-authentication-middleware/)
-   [A primer on OWIN cookie authentication middleware for the ASP.NET developer](https://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/)
-   [Authentication Filters in ASP.NET Web API 2 | The ASP.NET Site](http://www.asp.net/web-api/overview/security/authentication-filters)
