Forms Authentication 再紀錄

MVC5, FormsAuthentication 再紀錄。

需求

  1. SSO登入。

  2. 登入後更換Session。

  3. 使用Forms Authentication

  4. 登出後更換Session。

設定Web.Config驗證模式為:Forms

Weg.config
<configuration>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/Home/Index" timeout="30" requireSSL="true" />
      <!-- timeout 預設:30分鐘; 2880分鐘 = 2天; 8640分鐘 = 6天 --> 
      <!-- requireSSL,預設:false; 上線再設為true,開發期設為false --> 
    </authentication>  
  </system.web>
</configuration>

程式碼紀錄登入與登出

AccountController.cs
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Web.SessionState;

namespace YourProject.Controllers
{
    public class AccountController : Controller
    {
        /// 本例為SSO登入。驗 signonKey 並取得授權資料 
        [HttpGet]
        public ActionResult SSO(string id /* signonKey */)
        {
            //# 模擬登入驗證與取得授權資料
            //  一般是登入帳密檢查與取得授權資料,本例為SSO登入。
            AuthInfo auth = SimsLogonWithSSO(id);
            // userInfo, roles, funcList, authToken

            #region 驗證後登入前(註記)更換 Session。
            var manager = new SessionIDManager();
            string oldSessionId = this.Session.SessionID;
            string newSessionId = manager.CreateSessionID(System.Web.HttpContext.Current);
            bool redirected, isAdded;
            manager.SaveSessionID(System.Web.HttpContext.Current, newSessionId, out redirected, out isAdded);
            /// ※更探Session同時,基本上全部暨有的會話資料(session, cookie,etc.)全部不見!
            #endregion

            // 計算出門票,不讓ticket流向前端。
            // ※利用newSessionId的長亂數字串,再加上日期或主機資訊算出唯一性的ticket。
            string authTicket = $"{DateTime.Today.GetHashCode().ToString("x")}{newSessionId}";
            // 將AuthInfo暫存入AppDomain。
            AppDomain.CurrentDomain.SetData(authTicket, auth);

            //※ 導向新頁面這樣新Session才有效。
            return RedirectToAction("SSO2");
        }

        /// 將授權資料存入新 Session 並製作 FormsAuthentication cookie 與啟用。
        [HttpGet]
        public ActionResult SSO2()
        {
            try
            {
                #region ## 取得成功驗證資料
                // 計算出門票,不讓ticket流向前端。
                string authTicket = $"{DateTime.Today.GetHashCode().ToString("x")}{Session.SessionID}";
                // 將AuthInfo從AppDomain取出
                AuthInfo auth = (AuthInfo)AppDomain.CurrentDomain.GetData(authTicket);
                // AppDomain取出值後清空
                AppDomain.CurrentDomain.SetData(Session.SessionID, null);
                #endregion

                #region ## 註記入 Session
                Session.Clear();
                Session["AuthInfo"] = auth;
                // Session["LoginAuthToken"] = authToken;
                // Session["LoginUserInfo"] = loginUser;
                // Session["LoginUserMenu"] = menuInfo;
                #endregion

                #region ## 寫入 FormsAuthentication cookie --- 客制化
                /// 將使得 @Request.IsAuthenticated == true ;
                /// 將使得 [Authorize] 有作用

                string userData = "This is the user's private data or authToken, and will been encrypted";
                bool isPersistent = false; // (rememberMe == "yes"); // 記住我/永續性
                DateTime issueDate = DateTime.Now;
                DateTime expiresDate = issueDate.Add(FormsAuthentication.Timeout);

                //# auth ticket
                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                    2,
                    auth.userId,
                    issueDate,
                    expiresDate,  // ticket 到期日
                    isPersistent, //永續性
                    userData,     // 加入客制化資料
                    FormsAuthentication.FormsCookiePath
                    );

                //# new auth cookie
                string encTicket = FormsAuthentication.Encrypt(ticket);
                var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
                {
                    Secure = FormsAuthentication.RequireSSL, 
                    Expires = isPersistent ? DateTime.MinValue : expiresDate, // cookie 到期日
                    HttpOnly = true
                };

                //# 寫入auth cookie,客制化用
                Response.Cookies.Add(cookie);

                #endregion

                // return home page; 
                TempData["ErrMsg"] = new ErrMsg { errType = ErrTypeEnum.Success, errMsg = "SSO驗證成功。" };
                return RedirectToAction("Index", "Home");
            }
            catch (Exception ex)
            {
                // 失敗處理…
                TempData["ErrMsg"] = new ErrMsg { errType = ErrTypeEnum.Exception, errMsg = "SSO驗證失敗!", exception = ex };
                return RedirectToAction("Index", "Home");
            }
        }

        public ActionResult Logout()
        {
            //clear session &cookie
            Response.Cookies.Clear();
            Session.Clear();
            Session.Abandon();
            Session.RemoveAll();
            //※ 將會清掉己知所有『連線訊息』包含TempData也是。

            // sign out
            FormsAuthentication.SignOut();

            // 登出後更換 Session
            var manager = new SessionIDManager();
            string oldSessionId = this.Session.SessionID;
            string newSessionId = manager.CreateSessionID(System.Web.HttpContext.Current);
            bool redirected, isAdded;
            manager.SaveSessionID(System.Web.HttpContext.Current, newSessionId, out redirected, out isAdded);

            return View();
            //return RedirectToAction("Index", "Home"); // 回首頁
        }
    }
}

Last updated