ASP.NET Internet安全Forms身份驗證方法
ASP.NET 安全性的工作原理
網(wǎng)站在安全性方面有一個常見的要求:特定的頁面僅允許某些成員或其他經(jīng)過身份驗證的用戶瀏覽.充分利用Forms身份驗證是最好的方式.
身份驗證
從實現(xiàn)機制來說ASP.NET1.1與ASP.NET2.0的安全模型是一致的.首先配置網(wǎng)站為Forms 身份驗證模式,之后用戶訪問網(wǎng)站的URL,Forms 身份驗證系統(tǒng)會將未經(jīng)身份驗證的請求重定向到指定的登錄頁.用戶輸入憑據(jù)(用戶名密碼)并提交該頁.如果驗證程序驗證用戶的身份合法,則系統(tǒng)會向客戶端發(fā)出一個特定 Cookie(.NET1.1不支持無Cookie模式),它代表用戶的身份驗證票據(jù).這樣后續(xù)的請求中,客戶端瀏覽器會把該Cookie一同發(fā)送致服務(wù)器,如果該Cookie有效則用戶通過身份驗證并允許對原始請求的資源的訪問.
授權(quán)
如果用戶的請求被驗證通過了,但是他請求的URL是否允許用戶訪問了呢,這就用到了授權(quán).可以通過應(yīng)用程序配置文件來進行授友也可以在程序中使用代碼來驗證用戶是否有資格訪問該資源.如果授權(quán)失敗,則 ASP.NET 將用戶重定向到登錄頁.如果用戶已被授權(quán),則將允許用戶訪問受保護資源.
ASP.NET1.1實現(xiàn)方式
ASP.NET1.1的實現(xiàn)方式非常簡單,不過我們還是需要手寫一些代碼的,下面我們就一步一步地實現(xiàn).應(yīng)用程序配置節(jié)的詳細說明請參考MSDN相關(guān)文檔.
l 配置應(yīng)用程序使用 Forms 身份驗證,編輯web.config文件
<configuration>
<system.web>
<authentication mode="Forms">
<forms name=".ASPXCOOKIEAUTH" loginUrl="Login.aspx" protection="All" timeout="30" path="/" />
</authentication>
<authorization>
<deny users="?" /> <!—拒絕匿名 -->
</authorization>
......
</system.web>
<location path="Admin"><!—配置授權(quán),只允許擁有Admins角色的用戶訪問這個目錄下的文件(*.aspx)-->
<system.web>
<authorization>
<allow roles="Admins"/><!—雖然下面配置為拒絕所有用戶,但是allow的優(yōu)先級比deny高.-->
<deny users="*" /><!—拒絕所有用戶 -->
<!—
一個用戶或角色必須特別指定為拒絕,才能拒絕該用戶或角色對URL的權(quán)限.如果上面的示例沒有指定<deny users="*" />元素,則將允許所有通過身份驗證的用戶訪問所請求的 URL,而不考慮其所屬的角色.
-->
</authorization>
</system.web>
</location>
</configuration>
l 創(chuàng)建登錄頁面Login.aspx
頁面預(yù)覽如下,代碼詳細參考本文附件的項目源碼.

創(chuàng)建用戶身份主體
ASP.NET1.1安全模型提供了四種授權(quán)方法,這四種方法都使用HttpContext.User對象進行驗證授權(quán).
l 使用應(yīng)用程序配置進行授權(quán),只有具有指定角色的用戶才能訪問web.config所在的文件夾與子文件夾
<authorization>
<allow roles="Admins"/>
<deny users="?"/>
</authorization>
l 使用PrinciplePermissionAttribute控制對類和方法的訪問,只允許角色為Admins的成員才能調(diào)用該方法
[System.Security.Permissions.PrincipalPermission(System.Security.Permissions.SecurityAction.Demand,Role=” Admins”)]
public static bool MethodName()
{
...
}
l 以編程方式使用PrinciplePermission類控制對代碼塊的訪問,只允許角色為Admins的成員調(diào)用Demand之后的代碼
public static bool MethodName()
{
System.Security.Permissions.PrincipalPermission perm = new System.Security.Permissions.PrincipalPermission(null, "Admins");
perm.Demand();
...
}
l 使用Iprincipal.IsInRole方法,只允許角色為Admins的成員運行if中的代碼,大部分情況我們都使用這種方法判斷用戶是否有權(quán)限.
public static bool MethodName()
{
if (HttpContext.Current.User.IsInRole("Admins"))
{
//some code
}
}
針對以上的特點,程序員必須在合適的地方創(chuàng)建HttpContext.User對象,以達到驗證模型的要求.開發(fā)人員必須編寫HttpApplication:: AuthenticateRequest事件.該事件的發(fā)生代表著用戶己經(jīng)通過Forms身份驗證.
在Global.asax中實現(xiàn)Application_AuthenticateRequest.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
string encryptedTicket = cookie.Value;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encryptedTicket);
//獲取在登錄驗證時加入驗證票據(jù)的用戶所擁有的角色,但真正開發(fā)時請不這樣做,建議從數(shù)據(jù)庫中獲取該用戶角色信息.
//因為Cookie本身有長度的限制,并且將用戶角色存儲到客戶端也不是安全的行為.
//大家想想如果Cookie不限制大小,那么它的尺寸大到幾MB或GB時,客戶端與服務(wù)器的每一次通迅,將是怎樣的一種情況了,呵呵.
//這里僅展示如何將角色信息加入到用戶主體GenericPrincipal中.
string[] roles = ticket.UserData.Split(new char[] { ',' });//獲取角色
FormsIdentity identity = new FormsIdentity(ticket);
System.Security.Principal.GenericPrincipal user = new System.Security.Principal.GenericPrincipal(identity, roles);
app.Context.User = user;
//app.Context.User = new System.Security.Principal.GenericPrincipal(new System.Web.Security.FormsIdentity(FormsAuthentication.Decrypt(cookie.Value)), new string[]{"Admins"});
}
}
或者在Global.asax中實現(xiàn)FormsAuthentication_Authenticate效果是一樣的.
void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs e)
{
HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
string encryptedTicket = cookie.Value;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encryptedTicket);
string[] roles = ticket.UserData.Split(new char[] { ',' });
FormsIdentity identity = new FormsIdentity(ticket);
System.Security.Principal.GenericPrincipal user = new System.Security.Principal.GenericPrincipal(identity, roles);
e.Context.User = user;
}
}
其實FormsAuthenticationModule會自動生成一個User對象,只不過這個對象的角色列表為空,它只能是代表通過身份驗證,而不能通過授權(quán),因為我們限制了目錄的訪問角色,所以開發(fā)人員必須實現(xiàn)上面代碼,才能滿足我們的要足,如果說你的網(wǎng)站僅需要通過身份驗證的話,就可不必實現(xiàn)這些方法了.
用戶在請求URL時,ASP.NET請求通道會連續(xù)觸發(fā)一堆的事件,這些事件完成了一系列任務(wù),其中就包括Forms身份驗證事件與授權(quán)事件.如下所示:
BeginRequest 請求開始事件
AuthenticateRequest 驗證通過事件 (上面兩段代碼就是在這個事件中被執(zhí)行)
PostAuthenticateRequest 用戶標(biāo)識己建立時發(fā)生 ASP.NET 2.0引入的事件,后面會講到.
AuthorizeRequest 當(dāng)安全模塊已驗證用戶授權(quán)時發(fā)生
....其它事件略;
正是這些事件的壘加促成了ASP.NET框架驗證模型的實現(xiàn), 而且通過完成上面的幾個步驟,網(wǎng)站內(nèi)容就己經(jīng)受到授權(quán)機制的保護了.
下面讓我們看看ASP.NE安全模型是如何做到授權(quán)的.
l ASP.NET 1.1 安全模型驗證授權(quán)的原理
在 ASP.NET 中,有兩種方式限制對資源訪問的權(quán)限:文件授權(quán)與URL 授權(quán),這里我們僅討倫后者.
URL 授權(quán)由 UrlAuthorizationModule 執(zhí)行,它將用戶和角色映射到 ASP.NET 應(yīng)用程序中的 URL.這個模塊可用于有選擇地允許或拒絕特定用戶或角色對應(yīng)用程序的任意部分(通常在web.config文件中為目錄指定授權(quán)用戶或角色)的訪問權(quán)限.
HTTP模塊是在ASP.NET框架默認(rèn)應(yīng)用程序配置文件中注冊的,如下:
下面簡單分析UrlAuthorizationModule的源碼,便可了解驗證模型是如何驗證在web.config中指定的授權(quán)規(guī)則.
UrlAuthorizationModule在應(yīng)用程序初始化時向HttpApplication::AuthorizeRequest事件(安全模塊已驗證用戶授權(quán)時發(fā)生)注冊委托代碼,該代碼內(nèi)部調(diào)用AuthorizationConfig::IsUserAllowed.方法實現(xiàn)截圖如下:
上面代碼又調(diào)用了AuthorizationConfigRule::IsUserAllowed方法,截圖如下:
由于HttpApplication::AuthorizeRequest事件是在HttpApplication::AuthenticateRequest事件之后執(zhí)行的(請看我提到過的事件列表), 在前面介紹的AuthenticateRequest事件中我們修改了Context.User對象,而且加入了角色信息,所以AuthorizeRequest事件在驗證用戶的權(quán)限時發(fā)現(xiàn)Context.User對象中什么都有,所以它才允許用戶訪問請求的資源,否則請求將被返回到指定的頁面.以上就是ASP.NET1.1的原理,怎么樣你理解了嗎?
ASP.NET2.0你仍可以用這樣的機制,但又增加新特性.下面就看看在ASP.NET2.0中是如何實現(xiàn)的吧!
ASP.NET 2.0 實現(xiàn)方式
ASP.NET2.0的實現(xiàn)方式與ASP.NET1.1實現(xiàn)方式大同小異,同樣支持前一版本的安全模型.不過又新增了成員資格與角色管理授權(quán)模型.這里就介紹ASP.NET2.0新增的內(nèi)容.
l 應(yīng)用程序配置新增屬性
<system.web>
<authentication mode="Forms">
//defaultUrl是ASP.NET2.0版本新增的屬性, 在驗證模型重定向URL時將重定向到的URL.默認(rèn)值為"default.aspx".
//雖然ASP.NET1.1版本沒有該屬性,但程序中的默認(rèn)為"default.aspx".還是ASP.NET2.0的配置更為靈活.
<forms loginUrl="logon.aspx" protection="All" name=".ASPXFORMSAUTH" path="/" defaultUrl="Index.aspx"></forms>
</authentication>
<authorization>
<deny users="?" />/*匿名用戶*/
</authorization>
</system.web>
l 使用成員資格驗證登錄
ASP.NET 成員資格主要用于ASP.NET Forms 身份驗證,配合ASP.NET2.0登錄控件可以不用寫任何代碼就能實現(xiàn)Forms身份驗證.
首先創(chuàng)建登錄頁Login.aspx,將登錄控件托入到窗體中即可,不用寫任何登錄事件代碼,相比ASP.NET1.1節(jié)省時間不是一點半點.
l 使用角色管理進行訪問授權(quán)
角色管理可以幫助您管理授權(quán),使您能夠指定應(yīng)用程序中的用戶可訪問的資源.角色管理可讓您通過將用戶分配到相應(yīng)角色來對其進行分組,從而更容易控制訪問權(quán)限.
1. 啟用角色管理
要啟用該功能,修改web.config文件在<system.web>配置節(jié)點內(nèi)增加子節(jié)點如下:
<roleManager enabled="true" cacheRolesInCookie="true" />
cacheRolesInCookie屬性代表是否緩存角色信息,這樣不用每次都從數(shù)據(jù)庫中獲取角色,以提高應(yīng)用程序的性能.
但是將角色放在Cookie里總是風(fēng)險的,它可能被篡改,然后用它訪問未被授權(quán)的資源.
不過可以在用戶每次登錄時,先使用Role API的DeleteCookie方法刪除這個緩存Cookie, 這樣可使風(fēng)險小一點.
推薦代碼:
if(Membership.ValidateUser(username, password)){
Roles.DeleteCookie();
FormsAuthentication.RedirectFromLoginPage(username, false);
}
特別提示:應(yīng)用程序配置中當(dāng)角色管理可用并且提供者程序為AspNetSqlProvider時, SqlRoleProvider的GetRolesForUser方法會調(diào)用System.Web.DataAccess.SqlConnectionHelper類的私有靜態(tài)方法EnsureSqlExpressDBFile創(chuàng)建一個空的aspnetdb.mdf本地數(shù)據(jù)庫,該數(shù)據(jù)庫包含成員資格與角色管理,所需要的表結(jié)構(gòu)等信息.
2. 配置成員與角色
使用VS2008自帶的配置工具設(shè)定成員與角色是最簡單不過的了,點擊菜單欄中的[項目],下拉菜單的最后一項[ASP.NET配置],在彈出窗體中設(shè)置成員與角色關(guān)系.如下圖展示:
在安全選項卡內(nèi)有管理用戶與角色的內(nèi)容,如下圖:
本示例創(chuàng)建了一個用戶”iori”與一個角色”Admins”,并且指定了該用戶是Admins角色的成員.
另外該工具還會自動創(chuàng)建本地數(shù)據(jù)庫(如果還沒創(chuàng)建).與它相關(guān)的配置在machine.config文件中指定,如下圖所示,你可以更改數(shù)據(jù)庫的文件名,默認(rèn)為”aspnetdb.mdf”.
好了,通過完成上面的幾個步驟,網(wǎng)站內(nèi)容就己經(jīng)受到授權(quán)機制的保護了,可以用剛剛添加的用戶試試登錄吧.
相比上一版本,ASP.NET2.0在Forms身份驗證里為開發(fā)人員節(jié)省很多時間,幾乎不用開發(fā)人員寫任何代碼,方便了許多,下面讓我們一探究竟.
l ASP.NET 2.0 安全模型驗證授權(quán)的原理
1. 驗證原理
在.net1.1中開發(fā)人員必須為登錄頁編寫登錄事件,用來驗證用戶輸入的用戶名與密碼是否有效,而ASP.NET 2.0中引入了成員資格提供程序與標(biāo)準(zhǔn)登錄服務(wù)器控件,它們隱式使用Forms 身份驗證,登錄控件己經(jīng)包含了驗證用戶名的程序邏輯,也就是說登錄控件會把用戶輸入的用戶名與密碼自動與成員資格數(shù)據(jù)庫中的用戶進行匹配,如果成功匹配就將特定Cookie寫入客戶端.
2. 授權(quán)原理
還是拿UrlAuthorizationModule說事兒, 如果不啟用角色管理,實現(xiàn)方式與ASP.NET1.1差不多,不過由于ASP.NET2.0加入了角色管理模型,角色管理模型使用兩個類: RolePrincipal與RoleManagerModule來實現(xiàn)角色授權(quán).如果應(yīng)用程序配置的角色管理可用時,這兩個新對象將被應(yīng)用到aspx頁面的生命周期中, 由于RoleManagerModule 被初始化時會向HttpApplication對象的事件PostAuthenticateRequest加載委托代碼,該代碼會將app.Context.User對象包裝成RolePrincipal對象.
PostAuthenticateRequest事件是在ASP.NET 2.0中加入的,該事件發(fā)生在AuthenticateRequest事件之后,代表安全模塊已建立了用戶標(biāo)識,所以在這個事件中使用用戶標(biāo)識重新生成RolePrincipal對象.
下面為委托代碼的節(jié)選.
......//省略若干代碼
HttpApplication application = (HttpApplication) source;
HttpContext context = application.Context;
if (this._eventHandler != null)
{
RoleManagerEventArgs e = new RoleManagerEventArgs(context);
this._eventHandler(this, e);
if (e.RolesPopulated)
{
//判斷開發(fā)人員是否在Global.asax中寫了事件處理程序,如下顯示的代碼.
/*
//這里演示如何在Global.asax中自定義角色
void RoleManager_GetRoles(object sender, RoleManagerEventArgs e)
{
if (e.Context.Request.IsAuthenticated)
{
e.Context.User = new GenericPrincipal(new GenericIdentity(e.Context.User.Identity.Name), new string[] { "Admins" });
e.RolesPopulated = true;
}
}
*/
//如果e.RolesPopulated為真,代表開發(fā)人員自己創(chuàng)建了角色信息,
//RoleManagerModule就不會 生成RolePrincipal 對象了.
return;
}
}
......
if (!(context.User is RolePrincipal))
{
context.User = new RolePrincipal(context.User.Identity);
}
Thread.CurrentPrincipal = context.User;
注意:我們并沒有像ASP.NET1.1中那樣在AuthenticateRequest事件中生成User對象.但是User對象會在FormsAuthenticationModuleHTTP模塊中使用Forms身份驗證的特定Cookie重新被包裝成一個GenericPrincipal對象(角色為空).
在說明RolePrincipal對象有什么用之前,需要了解這個對象是何時被用到的.
在UrlAuthorizationModule被初始化時中向HttpApplication對象注冊的事件AuthorizeRequest被觸發(fā).
在這個事件中會調(diào)用RolePrincipal對象(就是Context.User)的方法IsInRole, IsInRole方法會自動查找角色提供程序(本示例使用默認(rèn)提供程序AspNetSqlProvider,數(shù)據(jù)庫為前面自動生成的ASPNETDB.MDF),并驗證用戶角色,代碼截圖如下:

而IsUserAllowed方法最終會調(diào)用RolePrincipal對角的IsInRole方法來判斷當(dāng)前用戶是否擁有某角色,方法截圖如下:
以上這些便是ASP.NET2.0成員資格與角色管理實現(xiàn)Forms身份驗證的原理與實現(xiàn),不過默認(rèn)的成員資格與角色數(shù)據(jù)庫的字段一般并不能滿足具體項目的需要.好在ASP.NET 2.0中提供了可擴展提拱程序模型,開發(fā)人員可以定制成員資格提供程序與角色管理模型.
結(jié)束語
網(wǎng)站應(yīng)用程序的身份驗證和授權(quán)方法是一項具有挑戰(zhàn)性的任務(wù),而Forms身份驗證在網(wǎng)站建設(shè)中提供了重要的安全性優(yōu)勢,通過提供用戶配置文件以及對角色的支持, 簡化了程序員通常需要編寫大量代碼才能完成的工作.如果讀者還有什么問題或者對以上描述有不同的見解,歡迎與我聯(lián)系互相交流!
- 淺談asp.net Forms身份驗證詳解
- 詳解ASP.NET MVC Form表單驗證
- 關(guān)于C#.net winform程序驗證moss的集成身份認(rèn)證實例
- Asp.Net二級域名共享Forms身份驗證、下載站/圖片站的授權(quán)訪問控制
- asp.net forms身份驗證,避免重復(fù)造輪子
- asp.net 基于forms驗證的目錄角色權(quán)限的實現(xiàn)
- asp.net Forms身份驗證和基于角色的權(quán)限訪問
- asp.net 特定目錄form驗證
- ASP.net Forms驗證Demo
- .net MVC使用IPrincipal進行Form登錄即權(quán)限驗證(3)
相關(guān)文章
asp net core2.1如何使用jwt從原理到精通(二)
這篇文章主要給大家介紹了關(guān)于asp net core2.1如何使用jwt從原理到精通的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11C#后臺調(diào)用前臺javascript的五種方法小結(jié)
于項目需要,用到其他項目組用VC開發(fā)的組件,在web后臺代碼無法訪問這個組件,所以只好通過后臺調(diào)用前臺的javascript,從而操作這個組件。2010-12-12Asp.net之TextBox只允許輸入數(shù)字的方法總結(jié)
Asp.net之TextBox只允許輸入數(shù)字的方法總結(jié),需要的朋友可以參考一下2013-02-02gridview實現(xiàn)服務(wù)器端和客戶端全選的兩種方法分享
這篇文章主要介紹了gridview實現(xiàn)服務(wù)器端和客戶端全選的兩種方法,需要的朋友可以參考下2014-02-02Convert.ToInt32與Int32.Parse區(qū)別及Int32.TryParse
2個方法都可以把string轉(zhuǎn)換為int,那么他們有什么區(qū)別?什么時候該用什么?性能如何。 其實在2.0里還有Int32.TryParse也實現(xiàn)了同樣的效果。2009-01-01