1. 程式人生 > >ASP.NET中的Forms和Windows混合驗證

ASP.NET中的Forms和Windows混合驗證

摘要:ASP.NET開發人員曾經問到過如何使用Forms和Windows混合驗證。Paul Wilson提供了一個解決方案來獲得Windows使用者名稱,或者,將使用者轉向登入頁面。

簡介

我曾經遇到很多ASP.NET開發人員問到如何使用Forms和Windows混合驗證。通常的回答是:“ASP.NET不支援混合驗證”。不談技術細節,讓我們先從業務的角度來看一下。我們要做的是自動取得域使用者名稱,並且將其他使用者轉向登入頁面。這樣可行嗎?當然。

Forms驗證

首先,需要確定要使用哪種ASP.NET驗證。想要使用Forms和Windows混合驗證並不容易,一個ASP.NET應用程式只能有一種驗證方式,所以你只能選擇其中一種。Windows驗證只能提供使用者名稱,就是ASP.NET的程序使用者,或者客戶端的使用者名稱(如果在IIS裡禁止了匿名登入的話)。明白了這個以後,很明顯,只有Forms驗證才是可以定製的。

我們來對程式中的web.config設定Forms驗證。需要指出的是,你要確定你的ASP.NET應用程式是一個IIS應用,對任何驗證方式這都是必須的。同時,需要在web.config裡設定拒絕匿名使用者。web.config中的authentication節點擁有一些屬性,loginUrl定義了未驗證使用者將重定向到的登入頁的URL。

接下來,你需要確定哪個頁面將作為ASP.NET應用的loginUrl。在所有我曾經看到過的Forms驗證中,是Login.aspx頁。但是,你想要首先通過Windows驗證使用者而不是使用登入頁面,所以,你的loginUrl應該是一個使用Windows整合驗證的頁面。於是,我們需要將Forms驗證的loginUrl設定到WinLogin.aspx.

IIS Windows驗證

接著,我們需要設定WinLogin.aspx頁面的Windows整合驗證。這裡一共有幾個步驟,包括,拒絕匿名訪問,獲取客戶端Windows憑證,獲取客戶端Windows使用者名稱,然後插入Forms驗證中。我們將在稍後處理Windows整合驗證失敗的情況。WinLogin.aspx只是用來測試整合Windows驗證,沒有html。

我們來看如何拒絕匿名訪問和取得客戶端Windows證書。這個問題的描述將會使你明白IIS已經擁有了這些功能,你可以直接使用它們來解決問題。開啟IIS,右鍵點選WinLogin.aspx檔案,開啟屬性設定,在“檔案安全性”標籤中,取消選中“匿名訪問”,然後選中“整合Windows身份驗證”來設定這個檔案的訪問控制。

但是,這樣並不能自動取得使用者名稱,我們還需要做一些設定。通過跟蹤頁面,或者反編譯WindowsAuthenticationModule中的OnEnter方法,你可以找到解決方法。使用者名稱儲存在名為LOGON_USER伺服器端變數中。你只需要通過Request.ServerVariables["LOGON_USER"]取得使用者名稱,然後呼叫FormsAuthentication.RedirectFromLoginPage將其插入Forms驗證即可,這樣就設定了驗證cookie同時轉向源頁面。

Windows整合驗證

IIS 401錯誤

現在,整合Windows驗證的工作已經完成了。但是,我們還需要處理Windows驗證失敗的狀況,應該將他們轉向使用者登入畫面來進行驗證。需要注意,Windows整合驗證依賴於瀏覽器,使用非IE瀏覽器的使用者可以在顯示登入框時選擇取消,這會導致Windows整合驗證失敗。我們還要明白在Windows整合驗證失敗時將會發生什麼。

Windows整合驗證失敗時,使用者會收到一個401錯誤。ASP.NET在web.config中內建了一個特性來獲取和轉向大部分錯誤,但是,401和403錯誤例外,必須通過其他方法來處理。我不知道用什麼方法可以處理401錯誤而不需要同IIS發生關係。

開啟IIS,右鍵點選WinLogin.aspx檔案,開啟屬性設定,在“自定義錯誤”標籤中,為所有的401型別錯誤設定一個自定義轉向。在這裡,轉向檔案必須是一個靜態頁面,而不能是ASP.NET頁面。我的解決方法是,將轉向路徑設定到Redirect401.htm檔案的實體地址,這個檔案中包含一段javascript或者meta標籤,來自動轉向到ASP.NET登入頁面WebLogin.aspx。注意,因為IIS錯誤轉向需要一個靜態頁面,會失去原始的ReturnUrl,所以你需要在將來處理這個問題。

Redirect 401 errors to a custom error page

頁面使用者登入

接下來,需要建立Forms驗證的登入頁面,命名為WebLogin.aspx。這個頁面只要需要包含讓使用者輸入使用者名稱和密碼的文字框,以及登入按鈕,也許你還需要其他的一些登入相關的屬性。還要在登入按鈕的時間中加入驗證程式碼,示例程式碼中只是簡單的測試使用者名稱和密碼是否相同。你也許還需要新增對自定義角色或者Windows角色的支援。

現在,已經取得驗證過的使用者,以及相應的使用者名稱。你可以通過呼叫FormsAuthentication.RedirectFromLoginPage來講這些資訊加入到Forms authentication中,但是由於我們已經丟失了原始的ReturnUrl,所以它看起來並不是很好。我們需要單獨設定驗證cookie,使用FormsAuthentication.SetAuthCookie然後手動轉向到原始的ReturnUrl,我們馬上會解決這個。看起來應該可以了,但是你會發現重新進入了WinLogin.aspx。

為什麼沒有進入WebLogin.aspx頁面?因為還沒有設定合適的許可權。你已經設定了Forms驗證而且拒絕匿名訪問,這可以自動的將匿名使用者轉向到loginUrl,也就是WinLogin.aspx。所以,你還需要設定WebLogin.aspx的許可權來允許匿名使用者訪問,通過web.config中的location節點,可以很容易的進行設定。現在,你已經成功的設定了Forms和Windows混合驗證。

重定向原始的Url

現在,唯一的問題就是追蹤原始的Url,來進行重定向。Forms驗證模組自動為WinLogin.aspx添加了一個ReturnUrl的querystring,但是整合Windows驗證拒絕非Windows使用者的訪問。然後,IIS將你轉向到一個靜態的html檔案,querystring會丟失。你需要使用一些更持久的東西來追哦那個原始的Url,比如cookies或者session。我們需要知道如何獲取原始的Url和儲存它。

Forms驗證使用HttpModule來獲取和處理所有的請求,在原始的請求頁面載入前將未驗證的使用者轉向。這意味著你需要在更早一些的時間來處理請求,比如在global.asax裡,你可以處理其中的AuthenticateRequest事件來獲取原始的Url。所以,檢查請求是否經過驗證,然後將請求的路徑放進ReturnUrl cookie中,這樣你就可以在WebLogin.aspx頁面中使用它來轉向了。

看起來沒問題了,但是你會重新進入WebLogin.aspx,而不是原始的URL。debug一下很容易就會發現問題所在,在請求WebLogin.aspx時你重寫了ReturnUrl cookie,所以對這種情況不要修改cookie。這樣,會有一個新的情況,使用者可以直接進入WebLogin.aspx,這時沒有ReturnUrl cookie,你需要檢查和處理這個問題。現在,你已經成功的設定了Forms和Windows混合驗證,而且有了原始的轉向。

Mixing Forms and Windows security in ASP.NET

總結

讓我們再看看我們都做了些什麼,為什麼看起來那麼困難。我們的問題是使用Forms和Windows混合驗證。開發人員傾向於跳過技術細節,告訴你不能使用混合驗證。但是使用者不關心有多少驗證方式,為什麼我們要在意?我們需要仔細看一下使用者需要的是什麼,在這個問題裡就是,獲取他們的Windows使用者名稱,或者將他們轉向到登陸頁面。

關於作者

Paul Wilson is a software architect in Atlanta who recently joined the development team at PRG-Schultz. His WilsonWebForm control allows multiple forms and non-post-back forms in ASP.NET. He is a Microsoft Most Valuable Professional in ASP.NET, a moderator of Microsoft's ASP.NET Forums, a Microsoft Certified Solution Developer, a Microsoft Certified Application Developer, a Microsoft Certified Data Base Administrator, and a Microsoft Certified Systems Engineer. Visit his website, http://www.WilsonDotNet.com, or e-mail him at [email protected].