2018年3月25日 星期日

將 ASP.NET MVC 5 原始碼加入現行專案中偵錯

環境:

Win7
Visual Studio 2017 Community
.NET Framework 已安裝好 4.5.2 以上版本

(因為相依性的關係,好像必需要裝 4.5.x 以上的 .NET Framework,未測試過,僅提供參考)

google 上搜尋 「asp.net mvc 5 github」 就可以找到 asp.net mvc 5 在 github 上的連結

要注意不要選到 asp.net core mvc 的那個

進入 github 後,github 頁面上應該顯示 README.md 的內容,內容上應該會說明各個 brance 和 tag 中包含的是那個版本的 mvc,和包含了那些其他的函式庫。



此次下載的是 tag 3.2.4 的版本


開一個新的方案

加入 asp.net mvc 原始碼的專案

    加入下面 5 個現有專案 ( 位置在asp.net mvc 原始碼包的 src 資料夾中 )

    System.Web.Mvc
    System.Web.Razor
    System.Web.WebPages
    System.Web.WebPages.Deployment
    System.Web.WebPages.Razor

加入一個新的 asp.net mvc 專案


進行以下調整

先確認 5 個 mvc 專案中的參考都是正常的,如果有參考有問題先處理
通常應該是重新加入參考或是從 nuget 裡解除安裝再重新安裝就可以了。

下面例子裡可以看到 Microsoft.Web.Infrastructure 參考有問題,處
理的方式就是從 nuget 裡重裝就行了


接下來將剛剛新增的 asp.net mvc 專案中的參考移除,準備用原始碼的專案取代

移除下面 5 個參考,再透過加入專案參考的方式加入



到這一步整個方案應該是可以正常編譯成功,但是 f5 啟動網站時會出現


右鍵屬性開啟剛剛加入的 5 個 asp.net mvc 原始碼的專案,在「簽署」的頁籤中,將「簽署組件」的項目取消掉,這 5 個專案都要做一次。


都取消簽署組件之後,將 System.Web.WebPages 中的 Properties 下的 AssemblyInfo.cs 打開


將 System.Web.Mvc 的 PublicKey 移掉


打開新增的 asp.net mvc 專案,在 Views \ Web.Config 裡面的 public key 移掉


接著應該就可以利用 F12 的方式去追踨 asp.net mvc 5 的原始碼了

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");
            }
        }

2018年3月14日 星期三

ASP.NET MVC FormsAuthenticationTicket 驗證

透過 FormsAuthenticationTicket 進行使用者是否登入的驗證

FormsAuthenticationTicket 此功能主要在記錄使用者是否已經登入,實際上
會產生一組 Cookie,內含一組經過加密的編碼,.net 可以透過檢核這組編碼,
來判斷使用者是否已經登入(或者說已獲得允許使用的權限)。

使用者在開啟頁面,程式進入 Action 前,先檢查使用者登入的驗證,也就是指
是否已取得 ticket,若未通過驗證,則跳轉到登錄頁,若已驗證,則允許進入
Action 內。

當使用者已經在登錄頁登錄,且檢核通過,則產生一張 ticket (也就是一組 Cookie),
當使用者切換頁面時,瀏覽器便會再把這組 ticket(Cookie),帶到後端,後端只
要檢核這組 ticket,就可以得知使用者是否仍處於已驗證的狀態。

1.在要進行驗證的 Action 前掛上 [Authorize()]
    [Authorize()]
    public ActionResult Home2()
    {
        return View();
    }
2.在 WebConfig 中加上,當驗證失敗時要導向的頁面位置(要加在專案下的那個 WebConfig)
    <system.web>
        <authentication mode="Forms">
            <forms loginUrl="Login/Index" cookieless="UseCookies" timeout="2880" />
        </authentication>
    </system.web>
3.當使用者登入後,寫入一張 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
                "",                                     //要與票證一同存放的使用者特定資料
                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");
        }
    }