很多用戶在開發(fā) ASP.NET 應(yīng)用程序時都有這樣的需求:管理員角色的賬戶使用管理員的登錄界面進(jìn)行登錄,普通用戶角色的賬戶使用普通用戶的登錄界面進(jìn)行登錄。由于ASP.NET的web.config里只能使用一個 authentication mode="Forms" 節(jié)點(diǎn),所以,要實(shí)現(xiàn)不同用戶采用不同的登錄界面,一個辦法就是創(chuàng)建一個管理員專用的虛擬目錄,并設(shè)置為應(yīng)用程序來實(shí)現(xiàn)。下面介紹另外一種采用重定向的辦法來解決這個問題。
本文介紹的方法原理是根據(jù)登錄界面的返回地址進(jìn)行判斷,然后重定向到不同的頁面。下面就是實(shí)現(xiàn)的詳細(xì)過程。
1,創(chuàng)建一個網(wǎng)站,在網(wǎng)站里創(chuàng)建Admin文件夾和User文件夾,分別存放admin和普通用戶所使用的文件。也可以只設(shè)置一個 Admin 文件夾。由于本方法采用的判斷返回路徑的方法,所以,要能從路徑中區(qū)分出哪些是admin用戶使用的文件夾。當(dāng)然,采用其他的判斷方法也是可以的。
2,在網(wǎng)站根目錄下分別創(chuàng)建3個登錄文件:Login.aspx、UserLogin.aspx和AdminLogin.aspx。其中Login.aspx文件起地址轉(zhuǎn)換的作用,Login.aspx文件的主要內(nèi)容:
- protected void Page_Load(object sender, EventArgs e)
- {
- String ReturnUrl = Request.QueryString["ReturnUrl"];
- if (ReturnUrl == null || ReturnUrl.Equals(String.Empty))
- {
- //默認(rèn)情況下,按普通用戶進(jìn)行登錄
- 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這個文件的內(nèi)容如下:
- <%@ 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)
- {
- //密碼驗(yàn)證過程在此省略,假如用戶名是mxh,密碼是mengxianhui
- String UserName = "mxh";
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
- UserName,//與身分驗(yàn)證票關(guān)聯(lián)的用戶名
- DateTime.Now, //票證發(fā)出時的本地日期和時間
- DateTime.Now.AddHours(1),//票證過期的本地日期和時間
- true,// 如果票證存儲在持久性cookie中(跨瀏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
- "reader",//儲存在票證中持定的用戶信息,本頁面供 reader 登錄使用
- FormsAuthentication.FormsCookiePath //票證儲存在cookie中的路徑
- );
- //如果 forms 元素的 protection 屬性設(shè)置為 All 或 Encryption,則窗體身份驗(yàn)證使用 Encrypt 方法對窗體身份驗(yàn)證票進(jìn)行加密和簽名。
- 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)
- {
- //密碼驗(yàn)證過程在此省略,假如用戶名是mxh,密碼是mengxianhui
- String UserName = "mxh";
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
- UserName,//與身分驗(yàn)證票關(guān)聯(lián)的用戶名
- DateTime.Now, //票證發(fā)出時的本地日期和時間
- DateTime.Now.AddHours(1),//票證過期的本地日期和時間
- true,// 如果票證存儲在持久性cookie中(跨瀏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
- "reader",//儲存在票證中持定的用戶信息,本頁面供 reader 登錄使用
- FormsAuthentication.FormsCookiePath //票證儲存在cookie中的路徑
- );
- //如果 forms 元素的 protection 屬性設(shè)置為 All 或 Encryption,則窗體身份驗(yàn)證使用 Encrypt 方法對窗體身份驗(yàn)證票進(jìn)行加密和簽名。
- 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>
這個文件將驗(yàn)證信息保存后,返回最初的請求頁面。注意:這里連接數(shù)據(jù)庫驗(yàn)證用戶名和密碼的過程省略過去了。
AdminLogin.aspx這個文件的全部內(nèi)容如下:
- <%@ 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)
- {
- //密碼驗(yàn)證過程在此省略,假如用戶名是Admin,密碼是mengxianhui
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
- "Admin",//與身分驗(yàn)證票關(guān)聯(lián)的用戶名
- DateTime.Now, //票證發(fā)出時的本地日期和時間
- DateTime.Now.AddHours(1),//票證過期的本地日期和時間
- true,// 如果票證存儲在持久性cookie中(跨瀏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
- "admin|manager|editor",//儲存在票證中持定的用戶信息,本頁面供 admin,manager,editor登錄使用
- FormsAuthentication.FormsCookiePath //票證儲存在cookie中的路徑
- );
- //如果 forms 元素的 protection 屬性設(shè)置為 All 或 Encryption,則窗體身份驗(yàn)證使用 Encrypt 方法對窗體身份驗(yàn)證票進(jìn)行加密和簽名。
- 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)
- {
- //密碼驗(yàn)證過程在此省略,假如用戶名是Admin,密碼是mengxianhui
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2,//票證的版本號
- "Admin",//與身分驗(yàn)證票關(guān)聯(lián)的用戶名
- DateTime.Now, //票證發(fā)出時的本地日期和時間
- DateTime.Now.AddHours(1),//票證過期的本地日期和時間
- true,// 如果票證存儲在持久性cookie中(跨瀏覽器會話保存)則為 true 否則為false 如果票證儲存在URL中,將忽略此值
- "admin|manager|editor",//儲存在票證中持定的用戶信息,本頁面供 admin,manager,editor登錄使用
- FormsAuthentication.FormsCookiePath //票證儲存在cookie中的路徑
- );
- //如果 forms 元素的 protection 屬性設(shè)置為 All 或 Encryption,則窗體身份驗(yàn)證使用 Encrypt 方法對窗體身份驗(yàn)證票進(jìn)行加密和簽名。
- 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>
注意:這里連接數(shù)據(jù)庫驗(yàn)證用戶名和密碼的過程省略過去了。
3,在Global的AuthenticateRequest 事件(一定要注意:不是 AuthorizeRequest 事件)里將角色信息附加到當(dāng)前用戶的上下文中。
- 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('|'); //讀出在登錄時設(shè)置的角色列表。
- System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id, roles);
- Context.User = principal;//將驗(yàn)證信息附加到當(dāng)前用戶上下文。
- }
- 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('|'); //讀出在登錄時設(shè)置的角色列表。
- System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id, roles);
- Context.User = principal;//將驗(yàn)證信息附加到當(dāng)前用戶上下文。
- }
4,在web.config文件中,允許登錄文件的匿名訪問,以便在未登錄的情況下顯示登錄界面,注意:如果包含圖片、css等文件,也需要設(shè)置這些資源允許匿名訪問。
- <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,這樣,當(dāng)訪問admin文件夾下的內(nèi)容時,會直接轉(zhuǎn)到AdminLogin.aspx界面。在登錄之后,就可以在/Admin/文件夾下的頁面中使用下面的方法得到當(dāng)前登錄的用戶名和所具有的角色,根據(jù)角色來判斷當(dāng)前用戶是否有權(quán)操作:
- Response.Write("<li>當(dāng)前登錄用戶 = " + Page.User.Identity.Name);
- Response.Write("<li>admin = " + Page.User.IsInRole("admin"));
- Response.Write("<li>reader = " + Page.User.IsInRole("reader"));
- Response.Write("<li>當(dāng)前登錄用戶 = " + Page.User.Identity.Name);
- Response.Write("<li>admin = " + Page.User.IsInRole("admin"));
- Response.Write("<li>reader = " + Page.User.IsInRole("reader"));
為了簡單起見,可以寫一個Admin使用的基類頁面,統(tǒng)一在基類頁面中進(jìn)行權(quán)限的處理。