很多用戶在開發 ASP.NET 應用程序時都有這樣的需求:管理員角色的賬戶使用管理員的登錄界面進行登錄,普通用戶角色的賬戶使用普通用戶的登錄界面進行登錄。由於ASP.Net的web.config裡只能使用一個 authentication mode="Forms" 節點,所以,要實現不同用戶采用不同的登錄界面,一個辦法就是創建一個管理員專用的虛擬目錄,並設置為應用程序來實現。下面介紹另外一種采用重定向的辦法來解決這個問題。
本文介紹的方法原理是根據登錄界面的返回地址進行判斷,然後重定向到不同的頁面。下面就是實現的詳細過程。
1,創建一個網站,在網站裡創建Admin文件夾和User文件夾,分別存放admin和普通用戶所使用的文件。也可以只設置一個 Admin 文件夾。由於本方法采用的判斷返回路徑的方法,所以,要能從路徑中區分出哪些是admin用戶使用的文件夾。當然,采用其他的判斷方法也是可以的。
2,在網站根目錄下分別創建3個登錄文件:Login.aspx、UserLogin.aspx和AdminLogin.aspx。其中Login.aspx文件起地址轉換的作用,Login.ASPx文件的主要內容:
protected void Page_Load(object sender, EventArgs e)
{
String ReturnUrl = Request.QueryString["ReturnUrl"];
if (ReturnUrl == null || ReturnUrl.Equals(String.Empty))
{
//默認情況下,按普通用戶進行登錄
Response.Redirect("~/UserLogin.ASPx");
}
else
{
if (ReturnUrl.ToLower().Contains("/admin/"))
{
Response.Redirect("~/AdminLogin.ASPx?ReturnUrl=" + Server.UrlEncode(ReturnUrl));
}
else
{
Response.Redirect("~/UserLogin.ASPx?ReturnUrl=" + Server.UrlEncode(ReturnUrl));
}
}
在這個文件的代碼中,如果ReturnUrl中含有"/admin/",就重定向到AdminLogin.aspx登錄界面;否則,就重定向到 UserLogin.ASPx 登錄界面。
UserLogin.ASPx這個文件的內容如下:
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-transitional.dtd">
<mce:script runat="server"><!--
protected void Button1_Click(object sender, EventArgs e)
{
//密碼驗證過程在此省略,假如用戶名是mxh,密碼是mengxianhui
String UserName = "mxh";
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
UserName,//與身分驗證票關聯的用戶名
DateTime.Now, //票證發出時的本地日期和時間
DateTime.Now.AddHours(1),//票證過期的本地日期和時間
true,// 如果票證存儲在持久性cookIE中(跨浏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
"reader",//儲存在票證中持定的用戶信息,本頁面供 reader 登錄使用
FormsAuthentication.FormsCookiePath //票證儲存在cookIE中的路徑
);
//如果 forms 元素的 protection 屬性設置為 All 或 Encryption,則窗體身份驗證使用 Encrypt 方法對窗體身份驗證票進行加密和簽名。
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookIEName, encTicket);
Response.Cookies.Add(cookIE);
Response.Redirect(FormsAuthentication.GetRedirectUrl(UserName, true));
}
// --></mce:script>
<html XMLns="http://www.w3.org/1999/xHtml">
<head runat="server">
<title>孟憲會之多用戶登錄測試頁面</title>
</head>
<body>
<form id="form1" runat="server">
普通用戶登錄界面省略<br />
<ASP:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="普通用戶登錄" />
</form>
</body>
</Html>
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-transitional.dtd">
<mce:script runat="server"><!--
protected void Button1_Click(object sender, EventArgs e)
{
//密碼驗證過程在此省略,假如用戶名是mxh,密碼是mengxianhui
String UserName = "mxh";
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
UserName,//與身分驗證票關聯的用戶名
DateTime.Now, //票證發出時的本地日期和時間
DateTime.Now.AddHours(1),//票證過期的本地日期和時間
true,// 如果票證存儲在持久性cookIE中(跨浏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
"reader",//儲存在票證中持定的用戶信息,本頁面供 reader 登錄使用
FormsAuthentication.FormsCookiePath //票證儲存在cookIE中的路徑
);
//如果 forms 元素的 protection 屬性設置為 All 或 Encryption,則窗體身份驗證使用 Encrypt 方法對窗體身份驗證票進行加密和簽名。
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookIEName, encTicket);
Response.Cookies.Add(cookIE);
Response.Redirect(FormsAuthentication.GetRedirectUrl(UserName, true));
}
// --></mce:script>
<html XMLns="http://www.w3.org/1999/xHtml">
<head runat="server">
<title>孟憲會之多用戶登錄測試頁面</title>
</head>
<body>
<form id="form1" runat="server">
普通用戶登錄界面省略<br />
<ASP:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="普通用戶登錄" />
</form>
</body>
</Html>
這個文件將驗證信息保存後,返回最初的請求頁面。注意:這裡連接數據庫驗證用戶名和密碼的過程省略過去了。
AdminLogin.ASPx這個文件的全部內容如下:
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-transitional.dtd">
<mce:script runat="server"><!--
protected void Button1_Click(object sender, EventArgs e)
{
//密碼驗證過程在此省略,假如用戶名是Admin,密碼是mengxianhui
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
"Admin",//與身分驗證票關聯的用戶名
DateTime.Now, //票證發出時的本地日期和時間
DateTime.Now.AddHours(1),//票證過期的本地日期和時間
true,// 如果票證存儲在持久性cookIE中(跨浏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
"admin|manager|editor",//儲存在票證中持定的用戶信息,本頁面供 admin,manager,editor登錄使用
FormsAuthentication.FormsCookiePath //票證儲存在cookIE中的路徑
);
//如果 forms 元素的 protection 屬性設置為 All 或 Encryption,則窗體身份驗證使用 Encrypt 方法對窗體身份驗證票進行加密和簽名。
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookIEName, encTicket);
Response.Cookies.Add(cookIE);
Response.Redirect(FormsAuthentication.GetRedirectUrl("Admin", true));
}
// --></mce:script>
<html XMLns="http://www.w3.org/1999/xHtml">
<head runat="server">
<title>孟憲會之多用戶登錄測試頁面</title>
</head>
<body>
<form id="form1" runat="server">
管理員登錄界面,省略
<ASP:Button ID="Button1" runat="server" Text=" 登 錄 " OnClick="Button1_Click" />
</form>
</body>
</Html>
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-transitional.dtd">
<mce:script runat="server"><!--
protected void Button1_Click(object sender, EventArgs e)
{
//密碼驗證過程在此省略,假如用戶名是Admin,密碼是mengxianhui
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
"Admin",//與身分驗證票關聯的用戶名
DateTime.Now, //票證發出時的本地日期和時間
DateTime.Now.AddHours(1),//票證過期的本地日期和時間
true,// 如果票證存儲在持久性cookIE中(跨浏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
"admin|manager|editor",//儲存在票證中持定的用戶信息,本頁面供 admin,manager,editor登錄使用
FormsAuthentication.FormsCookiePath //票證儲存在cookIE中的路徑
);
//如果 forms 元素的 protection 屬性設置為 All 或 Encryption,則窗體身份驗證使用 Encrypt 方法對窗體身份驗證票進行加密和簽名。
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookIEName, encTicket);
Response.Cookies.Add(cookIE);
Response.Redirect(FormsAuthentication.GetRedirectUrl("Admin", true));
}
// --></mce:script>
<html XMLns="http://www.w3.org/1999/xHtml">
<head runat="server">
<title>孟憲會之多用戶登錄測試頁面</title>
</head>
<body>
<form id="form1" runat="server">
管理員登錄界面,省略
<ASP:Button ID="Button1" runat="server" Text=" 登 錄 " OnClick="Button1_Click" />
</form>
</body>
</Html>
注意:這裡連接數據庫驗證用戶名和密碼的過程省略過去了。
3,在Global的AuthenticateRequest 事件(一定要注意:不是 AuthorizeRequest 事件)裡將角色信息附加到當前用戶的上下文中。
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
string cookieName = FormsAuthentication.FormsCookIEName;
HttpCookie authCookie = Context.Request.Cookies[cookIEName];
if (null == authCookIE)
{
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookIE.Value);
}
catch (Exception ex)
{
return;
}
if (null == authTicket)
{
return;
}
FormsIdentity id = new FormsIdentity(authTicket);
String[] roles = id.Ticket.UserData.Split('|'); //讀出在登錄時設置的角色列表。
System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id, roles);
Context.User = principal;//將驗證信息附加到當前用戶上下文。
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
string cookieName = FormsAuthentication.FormsCookIEName;
HttpCookie authCookie = Context.Request.Cookies[cookIEName];
if (null == authCookIE)
{
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookIE.Value);
}
catch (Exception ex)
{
return;
}
if (null == authTicket)
{
return;
}
FormsIdentity id = new FormsIdentity(authTicket);
String[] roles = id.Ticket.UserData.Split('|'); //讀出在登錄時設置的角色列表。
System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id, roles);
Context.User = principal;//將驗證信息附加到當前用戶上下文。
}
4,在web.config文件中,允許登錄文件的匿名訪問,以便在未登錄的情況下顯示登錄界面,注意:如果包含圖片、CSS等文件,也需要設置這些資源允許匿名訪問。
<configuration>
<location path="AdminLogin.ASPx">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
<location path="UserLogin.ASPx">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
<system.web>
<authentication mode="Forms">
<forms loginUrl="Login.ASPx" path="/" protection="Encryption"></forms>
</authentication>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
</system.web>
</configuration>
<configuration>
<location path="AdminLogin.ASPx">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
<location path="UserLogin.ASPx">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
<system.web>
<authentication mode="Forms">
<forms loginUrl="Login.ASPx" path="/" protection="Encryption"></forms>
</authentication>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
</system.web>
</configuration>
5,這樣,當訪問admin文件夾下的內容時,會直接轉到AdminLogin.ASPx界面。在登錄之後,就可以在/Admin/文件夾下的頁面中使用下面的方法得到當前登錄的用戶名和所具有的角色,根據角色來判斷當前用戶是否有權操作:
Response.Write("<li>當前登錄用戶 = " + Page.User.Identity.Name);
Response.Write("<li>admin = " + Page.User.IsInRole("admin"));
Response.Write("<li>reader = " + Page.User.IsInRole("reader"));
Response.Write("<li>當前登錄用戶 = " + Page.User.Identity.Name);
Response.Write("<li>admin = " + Page.User.IsInRole("admin"));
Response.Write("<li>reader = " + Page.User.IsInRole("reader"));
為了簡單起見,可以寫一個Admin使用的基類頁面,統一在基類頁面中進行權限的處理。