iT邦幫忙

5

[C#][ASP.NET] Web API 開發心得 (4) - 使用 FormsAuthentication 進行 API 授權驗證

今天要介紹在 Web API 使用 表單驗證 (FormsAuthentication) 進行授權驗證。

常見的驗證機制主要分為兩類:

  1. Cookie-Based Authentication: 使用瀏覽器的 Cookie 儲存使用者驗證資訊,此類驗證方式很早就有了,大部分網站的登入機制都是此類。

  2. Token-Based Authentication: 使用 Token 儲存使用者驗證資訊,Token 為一串加密文字,較 Cookie 靈活,可用於不支援 Cookie 的裝置上,這種驗證方式較新,主要是近幾年 APP 和社群網站興起,用於 API 的授權和驗證。

本篇會介紹第一種方式,Cookie-Based 雖然比較舊,但還是很好用的網站登入機制且較 Token-Based 簡單,之後有機會再介紹 Token-Based,我自己也還沒實作過,蠻想玩看看的。

下圖為網站登入流程圖。

https://ithelp.ithome.com.tw/upload/images/20180327/20106865ncaOHSsfOs.jpg

  1. 瀏覽器送出帳號和密碼做登入的動作
  2. 伺服器登入成功
  3. 伺服器將加密後帶有使用者資訊的 Cookie 寫回瀏覽器
  4. 瀏覽器向 API 請求資料並攜帶認證 Cookie
  5. 伺服器 Cookie 認證成功
  6. 伺服器回傳瀏覽器請求的資料

瀏覽器每次和 API 請求資料,都需要先驗證 Cookie 資訊,判斷使用者是否具有資料的存取權限。

實作

新增 Identity 列舉 (Enum) 管理使用者身分,使用 Enum 的優點 另一篇 文章有詳細說明,這裡就不再贅述。

public enum Identity
{
    [Description("管理者")]
    Admin = 1,

    [Description("一般使用者")]
    User = 2,
}

新增 User 類別定義要存入 Cookie 的資訊。

public class User
{
    //流水號
    public int Id { get; set; }
    //帳號
    public string UserId { get; set; }
    //名稱
    public string UserName { get; set; }
    //身分
    public Identity Identity { get; set; }
}

新增 AuthManager 類別管理登入、登出和取得使用者資訊的操作。

public class AuthManager
{
    //登入
    public void SignIn(User user)
    {
        //新增表單驗證用的票證
        var ticket = new FormsAuthenticationTicket(1,   //版本
            //使用者名稱
            user.UserName,
            //發行時間
            DateTime.Now,
            //有效期限
            DateTime.Now.AddMinutes(60),
            //是否將 Cookie 設定成 Session Cookie,如果是則會在瀏覽器關閉後移除
            false,
            //將要記錄的使用者資訊轉換為 JSON 字串
            JsonConvert.SerializeObject(user),
            //儲存 Cookie 的路徑
            FormsAuthentication.FormsCookiePath);

        //將 Ticket 加密
        var encTicket = FormsAuthentication.Encrypt(ticket);

        //將 Ticket 寫入 Cookie
        HttpContext.Current.Response.Cookies.Add(
            new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
    }

    //登出
    public void SignOut()
    {
        //移除瀏覽器的表單驗證
        FormsAuthentication.SignOut();
    }

    //取得使用者資訊
    public User GetUser()
    {
        //取得 ASP.NET 使用者
        var user = HttpContext.Current.User;

        //是否通過驗證
        if (user?.Identity?.IsAuthenticated == true)
        {
            //取得 FormsIdentity
            var identity = (FormsIdentity)user.Identity;

            //取得 FormsAuthenticationTicket
            var ticket = identity.Ticket;

            //將 Ticket 內的 UserData 解析回 User 物件
            return JsonConvert.DeserializeObject<User>(ticket.UserData);
        }
        return null;
    }
}

新增 SignInViewModel 類別接收傳回的帳號和密碼,ViewModel 的功能是接收前端傳回的資料,或回傳資料給前端,作為內部和外部溝通的橋樑。

public class SignInViewModel
{
    //帳號
    public string UserId { get; set; }
    //密碼
    public string Password { get; set; }
}

新增 AuthController 測試登入和登出相關功能。

[RoutePrefix("api/auth")]
public class AuthController : ApiController
{
    private AuthManager _authManager;
    public AuthController()
    {
        _authManager = new AuthManager();
    }

    //登入
    [HttpPost]
    [Route("signIn")]
    public void SignIn(SignInViewModel model)
    {
        //模擬從資料庫取得資料
        if (!(model.UserId == "abc" && model.Password == "123"))
        {
            throw new Exception("登入失敗,帳號或密碼錯誤");
        }

        var user = new User
        {
            Id = 1,
            UserId = "abc",
            UserName = "小明",
            Identity = Identity.User
        };
        _authManager.SignIn(user);
    }

    //登出
    [HttpPost]
    [Route("signOut")]
    public void SignOut()
    {
        _authManager.SignOut();
    }

    //測試是否通過驗證
    [HttpPost]
    [Route("isAuthenticated")]
    public bool IsAuthenticated()
    {
        var user = _authManager.GetUser();
        if (user == null)
        {
            return false;
        }
        return true;
    }
}

最後要在 Web.config 內加入 <authentication mode="Forms" /> 才會啟用表單驗證。

<configuration>
  <system.web>
    <!--啟用Form認證-->
    <authentication mode="Forms" />
  </system.web>
</configuration>

測試

接著使用 Postman 測試。

1.登入

api/auth/signIn

.ASPXAUTH 就是表單驗證使用的 Cookie。

https://ithelp.ithome.com.tw/upload/images/20180826/20106865sfjR7TSCa7.jpg

2.是否通過驗證

api/auth/isAuthenticated

回傳 true 表示成功登入。

https://ithelp.ithome.com.tw/upload/images/20180826/20106865HM7WPvVoWf.jpg

3.登出

api/auth/signOut

可以看到 Cookie 少了一個,.ASPXAUTH 被清除了。

https://ithelp.ithome.com.tw/upload/images/20180826/20106865pGA4BzIVp9.jpg

4.是否通過驗證

api/auth/isAuthenticated

回傳 false 表示成功登出。

https://ithelp.ithome.com.tw/upload/images/20180826/20106865FiWWk8iJlC.jpg

結語

表單驗證幫我們處理了加解密和 Cookie 的操作,因此很容易就完成了網站的登入機制,AuthManager 使用 JSON 儲存使用者資訊,JSON 容易擴展,未來如需增加 Cookie 資訊,只需對 User 類別新增欄位即可,不過還是要注意 Cookie 的長度限制,各家瀏覽器不太一樣,大概是 4K 左右,今天就介紹到這裡,感謝大家觀看。

參考文章

[ASP.NET WebApi]使用JWT進行web api驗證
浅谈使用Json Web Token和Cookie的利弊
簡介 ASP.NET 表單驗證 (FormsAuthentication) 的運作方式
ASP.NET 自訂角色的方式(不用實做 Role Provider)
[ASP.net MVC] ASP.net MVC整合FormsAuthentication表單驗證登入 - 簡易範例程式碼
請問有關Context.User.Identity 與 Request.IsAuthenticated 之間的問題
UserManager(Of TUser) 類別


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
michelletsai
iT邦新手 5 級 ‧ 2021-05-25 15:55:42

如何測試http://localhost/WebAPITest/api/auth/signIn
我測試都出現{
"Message": "要求的資源不支援 http 方法 'GET'。"
}
如何解決

看更多先前的回應...收起先前的回應...

送出的請求要選擇 POST 方法

https://ithelp.ithome.com.tw/upload/images/20210525/20106865W0d4a4Ilrx.jpg

方便請教一下,是 用哪個瀏覽器嗎?

如何出現選擇post的畫面。

謝謝,我發現是用postman測試了,謝謝您

是的,我是用 Postman 測試。

Jason iT邦新手 4 級 ‧ 2021-11-03 14:07:03 檢舉

請教一下if (user?.Identity?.IsAuthenticated == true)
?.是什麼意思,為何多一個?

C# 的 Null 條件運算子,相當於。

if (user != null &&
    user.Identity != null &&
    user.Identity.IsAuthenticated == true)

我要留言

立即登入留言