2

奇技淫巧 - ASP.NET 同時支援 Form 及 Windows 驗證

 1 year ago
source link: https://blog.darkthread.net/blog/mix-form-win-auth/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ASP.NET 同時支援 Form 及 Windows 驗證-黑暗執行緒

跟同事討論到 ASP.NET/IIS 有沒可能同時支援 Windows 驗證跟 Form 驗證?依我的理解,每個 Web Application 只能二者擇一,要嘛走 Windows 驗證、要嘛用 Form 驗證。討論到後來,我對自己發出靈魂拷問:ASP.NET 應用程式真的沒辦法做到使用 Form 驗證又能用 Windows 整合式驗證嗎?

過去我能想到的方法是開兩個 Web Appliation 分別跑 Form 驗證及 Windows 驗證,再自己寫 SSO 機制串接;或是 Form 接入 AD 帳號密碼再走 LDAP 或其他方式驗證帳號密碼(缺點是程式端會拿到 AD 帳密,需面對帳號密碼可能被儲存或外流的質疑,我個人偏好「閃開! 讓專業的瀏覽器跟 IIS 來處理」)。沒法像下面這樣,在原本使用 Form 驗證的 Web Applicaion 加一個鈕讓使用者改用 Windows 驗證登入:

Fig1_637893046294434301.gif

這... 所以,現在大家知道答案了,是的! 這是有可能做到的。

上面的範例是我參考網路文章實做出來的,整理重點如下:

  1. 使用 Form 或 Windows 驗證,由 web.config 的 system.web/authentiation 與 application.config system.webServer/security/authentication 控制 (延伸閱讀:【答客問】IIS 與 web.config 的 Windows 驗證設定),預設我們無法切換特定網址的 Windows 驗證,像是全站用 Form 驗證,只有 LoginAD.aspx 用 Windows 驗證:
    <?xml version="1.0"?>
     <configuration>
       <system.web>
         <compilation debug="true" targetFramework="4.7.2"/>
         <authentication mode="Forms">
           <forms loginUrl="Login.aspx"/>
         </authentication>
         <authorization>
           <deny users="?"/>
         </authorization>
         <pages controlRenderingCompatibilityVersion="4.0"/>
       </system.web>
     	<location path="LoginAD.aspx">
     		<system.webServer>
     			<security>
     				<authentication>
     					<windowsAuthentication enabled="true" />
     				</authentication>
     			</security>
     		</system.webServer>
     	</location>
     </configuration>
    
    將導致 503.19 錯誤:
    Fig2_637893046295037373.png
    但我們調整 IIS 設定:
    Fig4_637893046295788398.gif
    或修改 applicationhost.config 可解除此一限制:(衍生風險為無管理者權限者可以修改 web.config 避開 Windows 驗證,但前題要拿到更改網站檔案的權限)
    Fig3_637893046296673833.png
    (用 Visual Studio/IISExpress 測試的話,要改 .vs\ProjName\config\applicationhost.conf)
  2. 由於全站開 Form 驗證,預設連到 LoginAD.aspx 時也會被導向 Login.aspx,要靠 Global.asax 加入 Application_EndRequest() 發現 LoginAD.aspx 被導走時改傳 401:
    <%@ Application Language="C#" %>
    <%@ Import Namespace="System.Security.Principal" %>
    <script RunAt="server">
        void Application_EndRequest(object sender, EventArgs e)
        {
            if (Response.StatusCode == 302 &&
                Response.RedirectLocation.Contains("/Login.aspx") &&
                Response.RedirectLocation.ToLower().Contains("loginad") &&
                Request.Browser.Win32)
            {
                System.Diagnostics.Debug.WriteLine(Response.RedirectLocation);
                Response.ClearContent();
                Response.Write(string.Empty);
                Response.TrySkipIisCustomErrors = true;
                Response.Status = "401 Unauthorized";
                Response.StatusCode = 401;
                //.NET 4.5+
                Response.SuppressFormsAuthenticationRedirect = true;
            }
        }
    </script>
    
  3. LoginAD.aspx 的任務是取得 Windows 登入身分將其轉成 FormAuthentiation Cookie:
    <%@ Page Language="C#" %>
     <%@ Import Namespace="System.Security.Principal" %>
     <script runat="server">
         void Page_Load(object sender, EventArgs e)
         {
             Response.Cache.SetCacheability(HttpCacheability.NoCache);
             Response.Cache.SetNoStore();
             Response.SuppressFormsAuthenticationRedirect = true;
             if (!Request.IsAuthenticated || User.Identity is FormsIdentity)
             {
                 FormsAuthentication.SignOut();
                 Response.StatusCode = 401;
             }
             else
             {
                 var userId = User.Identity.Name.Split('\\').Last();
                 FormsAuthentication.SetAuthCookie(userId, false);
                 Response.Redirect("~/");
             }
         }
     </script>
    

總之透過以上精巧安排,我們就能在 Form 驗證加個按鈕,按下去改用 AD 帳號登入,感覺挺酷的,可適用除了 AD 帳號還有自訂帳號的應用場合。

範例為求簡便是用 WebForm 實作,但搬到 ASP.NET MVC 使用也是 OK 的。老樣子,範例程式已放上 Github,有需要的朋友請自取參考。

【參考資料】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK