2018年3月18日 星期日

ASP.NET MVC FormsAuthenticationTicket 驗證(2)

自訂 FormsAuthenticationTicket

當在進入 Action 前,需要是否已經登入以外的驗證,但又不想在每個 Action 裡面都加上一段驗證行為的程式碼。

此時可以利用自訂 FormsAuthenticationTicket 的方式處理。

原因在於產生 ticket 時,可以在 ticket 中加入自訂的資訊,讓 ticket 進行驗證的同時,透過這些自訂的資訊來完成額外的驗證。

如果不想用 FormsAuthenticationTicket 也可以自己做一個繼承 Attribute 類別的Attribute 掛在 Action 上,一樣的道理

例子裡是把驗證放在 OnAuthorization 這個方法中,還有另一個 AuthorizeCore 方法也可以用。

追溯 AuthorizeAttribute 的父類別,也是有繼承 Attribute,就如同自訂的 Attribute,也可在把它掛上 Action 時,傳入參數做處理。

比方說我需要驗證,在 ticket 中所記錄的 UserData 要包含某些特定字串,這個字串是在把Attribute 放上 Method 時定義好的。

1.建立一個新的類別,並繼承 AuthorizeAttribute
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public string Code { get; set; }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);            

            //判斷 ticket 是否有效(授權ok)
            if (filterContext.Result is HttpUnauthorizedResult &&
                !filterContext.HttpContext.Request.IsAjaxRequest())
            {
                //不是從 Html.RenderAction 進來的才能透過 RedirectToRoute 轉向
                if (!filterContext.IsChildAction)
                {
      //失敗後跳轉至
                    filterContext.Result = new RedirectToRouteResult(
                        new System.Web.Routing.RouteValueDictionary
                        {
                            {"controller", "Login"},
                            {"action","Home3"}
                        });
                }
                return;
            }
            else
            {
                //ticket 有效(授權ok),開始進行自訂的驗證的部份
                FormsIdentity id = filterContext.HttpContext.User.Identity as FormsIdentity;
                //取得 ticket
                FormsAuthenticationTicket ticket = id.Ticket;

                //建立 ticket 時的特定資訊
                string info = ticket.UserData;

                //透過這個資訊進行額外的驗證
  info.IndexOf(this.Code);
            }
        }
    }

2.因為在驗證的過程中,已經決定當驗證失敗時要跳轉到那個頁面,所以下面的設定其實可以不用加
<system.web>
        <authentication mode="Forms">
            <forms loginUrl="Login/Index" cookieless="UseCookies" timeout="2880" />
        </authentication>
    </system.web>

3.在想要進行驗證的 Action 前加上 Attribute
    [CustomAuthorizeAttribute(Code = "ABC")]
    public ActionResult Home()
    {
        return View();
    }

4.當使用者登入後,寫入一張 ticket,讓之後進入 action 前能通過 [Authorize()] 驗證
        public ActionResult Login(string uid)
        {
            //驗證使用者輸入的帳號密碼
            if (uid.ToUpper() == "UID")
            {
                //驗證通過,建立一張 ticket
                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                    1,                                      //票證的版本琥碼
                    uid,                                    //與票證相關的使用者名稱
                    DateTime.Now,                           //核發此票證時的本機日期和時間
                    DateTime.Now.AddMinutes(60),            //票證到期的本機日期和時間
                    false,                                  //如果票證將存放於持續性Cookie中,則為true
                    "ABC",                                  //要與票證一同存放的使用者特定資料
                    FormsAuthentication.FormsCookiePath);   //票證存放於Cookie中時的路徑

                //ticket 生成 cookie
                string encTicket = FormsAuthentication.Encrypt(ticket);
                HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
                httpCookie.HttpOnly = true;

                //cookie 寫入 response
                Response.Cookies.Add(httpCookie);

                //轉向到首頁
                return View("Home");
            }
            else
            {
                //驗證不通過,返回到登錄頁
                return View("Index");
            }
        }

沒有留言:

張貼留言