轉於http://www.cnblogs.com/stg609/archive/2009/03/12/1408296.html
asp.net Forms表單驗證 使用經驗及驗證流程分析
最近,要做一個登陸的頁面,就想到了安全性方面的問題。記得曾經在邵志東老師講的關於asp.net安全性方面的課程中,提到asp.net提供了4個身分識別驗證程式:1.表單身分識別驗證;2.Windows身分識別驗證;3.Passport身分識別驗證;4.預設身分識別驗證。尤其講了表單身分識別驗證,想想,正好自己以前也不曾使用過這個驗證方式,那就拿來練練手吧。
表單驗證,可以根據使用者和角色來限制使用者訪問。比如,我們有以一個後台管理系統,只有通過後台登陸頁面合法登陸的使用者才能訪問後台管理系統中的任何頁面,這個時候我們就可以通過表單驗證來實現(過去我都是在每一個頁面寫判斷邏輯。現在想起來,過去的那種方法真是不折不扣的體力勞動,而且如果哪個頁面忘記寫了,就麻煩了)。
實驗開始(因為只記錄經驗,所以有些知識點這裡並沒有提到,需要大家多花點課外時間了。文末提供了些連結供大家參考)
我接下來就來做一個Forms表單驗證的例子。在該例子中,我建立了兩個檔案夾分別為User和Admin,在每一個檔案夾中又有login.aspx、index.aspx和web.config。我希望普通使用者訪問User檔案夾需要首先要在使用者登陸介面進行登陸,成功後才能訪問使用者的index.aspx。而管理員則首先要在Admin的登陸介面進行登陸,才能訪問Admin中的index.aspx。而在網站根目錄有LoginRedirect.aspx、web.config和Global.asax。
目錄結構圖:
如何才能實現表單驗證呢?
1.配置根目錄下的web.config (在網站根目錄下web.config檔案中的system.web標記中,修改原<authentication mode="Windows" />為如下代碼)
<authentication mode="Forms">
<forms name="adminlogin" loginUrl="loginRedirect.aspx">
</forms>
</authentication>
<authorization>
<allow users="*"/>
</authorization>
上述的配置是什麼意思呢?
首先,這裡有兩個不同的配置節,authentication和authorization看上去是不是很像? 你可千萬不要被眼睛欺騙了,這兩個是不同的意思,前者是“驗證”,後者是“授權”。
接著,我們來看下“驗證”這個配置節中的東西。
mode表示的就是驗證方式,這裡有四個選項:Windows、Forms、Passport、None。預設的是Windows。我們這裡選擇Forms。
而在forms元素裡,設定了name和loginUrl。
name表示cookie的名字,我們後面要通過cookie來儲存一些使用者資訊並將包含cookie資訊的http請求發送到伺服器。伺服器端,則會根據cookie資訊對使用者進行標識,從而進行進一步的驗證。
LoginUrl 顧名思義就是登陸頁面的地址。如果說使用者沒有許可權訪問某一頁面,就會被重新導向到這個頁面。
還有其它諸多元素,請大家自己尋找相關資料。我也會在文末給出一些我認為比較不錯的連結。
講完了“驗證”節,接著講講“授權”節。
授權,自然是要限制哪些使用者或角色可以訪問,哪些使用者或角色不能訪問了。設定的方式就是通過設定<allow>和<deny>。如上所示的<allow users="*">就是表示允許所有使用者訪問。你可能會奇怪不是要限制使用者訪問嗎,怎麼全部允許了?那是因為,我就是希望“根目錄下”的所有東西都可以被任何使用者訪問。
再來看看兩個子檔案夾內的web.config。
<configuration>
<location path="login.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<system.web>
<authorization>
<allow roles="user"/>
<deny users="*"/>
</authorization>
</system.web>
</configuration>
在這個設定檔中,不能配置“驗證”節的內容(該內容只能在虛擬目錄的根目錄web.config中配置),我們只能對“授權”節進行配置。上述的location的作用是表示該路徑不需要進行授權檢查,因為我希望任何使用者都可以進到登陸頁面,原因大家應該都想得到吧。
而其他路徑,則不希望未登陸的使用者或網站管理員登陸,因此使用<allow roles="user">來允許只有角色為user的使用者訪問,而其他任何使用者都拒絕訪問。
同理,來看下管理員目錄的web.config,請大家自己分析。
<configuration>
<location path="login.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<system.web>
<authorization>
<allow roles="Manager"/>
<deny users="*"/>
</authorization>
</system.web>
</configuration>
配置好了之後,我們還需要寫一些cs代碼。首先我們來看一下loginRedirect.aspx.cs。因為,我們現在訪問上述任何一個子檔案的頁面時,都會先跳轉到這個頁面裡來,而其實我則希望可以跳到相信子目錄的登陸頁面中去。因此,需要在這個檔案中進行一些判斷。
string from = Request.QueryString["ReturnUrl"];//每個跳轉過來的頁面都會帶有ReturnUrl值,以此來擷取跳轉之前的頁面
//擷取子目錄名稱
string fromFilePath = from.Substring(from.IndexOf('/') + 1, from.IndexOf('/', from.IndexOf('/') + 1) - from.IndexOf('/')-1);
//根據子目錄名稱來判斷跳轉的連結
switch (fromFilePath.ToLower())
{
case "admin": Response.Redirect("/admin/login.aspx"); break;
case "user": Response.Redirect("/user/login.aspx"); break;
}
有些人可能奇怪了,這麼麻煩,既然可以在“驗證”節中配置loginUrl,難道就不能對每個目錄實現直接跳轉到本目錄相應登陸頁面嗎?很遺憾,目前為止,我還沒有找到直接的解決辦法。如果您有什麼辦法,請不吝賜教。
跳轉到登陸頁面後,那我們就應該對使用者的登陸時間進行處理了。
protected void Page_Load(object sender, EventArgs e)
{
//判斷使用者是否已經登陸,且角色為user
if (User.Identity.IsAuthenticated&&User.IsInRole("user"))
{//如果通過驗證,則直接跳轉到index.aspx
Response.Redirect("index.aspx");
}
}
//登陸按鈕事件,這裡簡單起見,我直接以使用者名稱"user",密碼"1"來判斷,當然你也可以從資料庫讀取。
protected void btnLogin_Click(object sender, EventArgs e)
{
if (tbUserName.Text == "user" && tbPwd.Text == "1")
{
//產生驗證票據,其中包括使用者名稱、生效時間、到期時間、是否永久儲存和使用者資料等。而關於使用者角色的資訊,我們儲存在使用者資料中。
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, tbUserName.Text, DateTime.Now, DateTime.Now.AddMinutes(30), true, "User");
string cookieStr = FormsAuthentication.Encrypt(ticket);//對票據進行加密
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieStr);
/*儲存到cookie中。cookie的名字要與我們前面在設定檔中所寫的name值一樣。因為,當cookie保留在本地後,下次再檢查使用者權限的時候就會自動尋找與forms名稱相同的cookie,並傳送給伺服器端進行檢驗。如果在本地找不到cookie,就自然無法通過驗證。*/
cookie.Expires = ticket.Expiration;
cookie.Path = FormsAuthentication.FormsCookiePath;
Response.Cookies.Add(cookie);
Response.Redirect("index.aspx");//登陸成功後跳轉到index.aspx
}
}
/*
這裡突然冒出一個票據,有些朋友是不是很奇怪呀?票據什麼用呢?
票據其實也可以理解為憑據(只有有憑據的使用者才能通過檢查),它包括了上面注釋中所寫的一些與使用者相關的資訊。但是票據不能直接傳送給伺服器必須通過cookie來承載。而伺服器端在接受到cookie之後,會從中取出票據的資料,並進行相關操作。
*/
在Admin檔案夾下的login.aspx.cs也是類似。就不再贅述了。
差點忘了最後的一個東西了,就是為我們的使用者配置角色。上面我們在建立票據的時候發現沒有提供直接對角色賦值的功能,那我們就只能利用grobal.asax來實現了。
在global.asax中有一個Application_AuthenticateRequest事件,該事件會在伺服器決定該使用者瀏覽器是否應該跳轉前發生。因此,我們只要在這裡對使用者角色進行配置,就可以達到目的。
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{//如果使用者通過驗證,則該項不為null
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
string userData = ticket.UserData;//取出角色資料
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);//重新分配角色
}
}
}
}
大家可以下載整個工程來看。
【注意:不能在同一台電腦上即登陸使用者介面又登陸管理員介面。因為使用Forms名稱所能保留的cookie只可能是一個,所以如果登陸了管理員介面後,接著登陸使用者介面,就會覆蓋原來的cookie值。有些朋友使用session來儲存使用者資料的方法,為實驗]