很多用户在开发 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文件的主要内容:
01 | protected void Page_Load(object sender, EventArgs e) |
03 | String ReturnUrl = Request.QueryString["ReturnUrl"]; |
04 | if (ReturnUrl == null || ReturnUrl.Equals(String.Empty)) |
07 | Response.Redirect("~/UserLogin.aspx"); |
11 | if (ReturnUrl.ToLower().Contains("/admin/")) |
13 | Response.Redirect("~/AdminLogin.aspx?ReturnUrl=" + Server.UrlEncode(ReturnUrl)); |
17 | Response.Redirect("~/UserLogin.aspx?ReturnUrl=" + Server.UrlEncode(ReturnUrl)); |
在这个文件的代码中,如果ReturnUrl中含有"/admin/",就重定向到AdminLogin.aspx登录界面;否则,就重定向到 UserLogin.aspx登录界面。
UserLogin.aspx这个文件的内容如下:
01 | <%@ Page Language="C#" %> |
02 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
04 | <mce:script runat="server"><!-- |
05 | protected void Button1_Click(object sender, EventArgs e) |
08 | String UserName = "mxh"; |
09 | FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, |
12 | DateTime.Now.AddHours(1), |
15 | FormsAuthentication.FormsCookiePath |
18 | string encTicket = FormsAuthentication.Encrypt(ticket); |
19 | HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); |
20 | Response.Cookies.Add(cookie); |
21 | Response.Redirect(FormsAuthentication.GetRedirectUrl(UserName, true)); |
26 | <title>孟宪会之多用户登录测试页面</title> |
29 | <form id="form1" runat="server"> |
31 | <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="普通用户登录" /> |
这个文件将验证信息保存后,返回最初的请求页面。注意:这里连接数据库验证用户名和密码的过程省略过去了。
AdminLogin.aspx这个文件的全部内容如下:
01 | <%@ Page Language="C#" %> |
02 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
04 | <mce:script runat="server"><!-- |
05 | protected void Button1_Click(object sender, EventArgs e) |
08 | FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, |
11 | DateTime.Now.AddHours(1), |
13 | "admin|manager|editor", |
14 | FormsAuthentication.FormsCookiePath |
17 | string encTicket = FormsAuthentication.Encrypt(ticket); |
18 | HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); |
19 | Response.Cookies.Add(cookie); |
20 | Response.Redirect(FormsAuthentication.GetRedirectUrl("Admin", true)); |
25 | <title>孟宪会之多用户登录测试页面</title> |
28 | <form id="form1" runat="server"> |
30 | <asp:Button ID="Button1" runat="server" Text=" 登 录 " OnClick="Button1_Click" /> |
注意:这里连接数据库验证用户名和密码的过程省略过去了。
3. 在Global的AuthenticateRequest 事件(一定要注意:不是 AuthorizeRequest 事件)里将角色信息附加到当前用户的上下文中。
01 | protected void Application_AuthenticateRequest(object sender, EventArgs e) |
03 | string cookieName = FormsAuthentication.FormsCookieName; |
04 | HttpCookie authCookie = Context.Request.Cookies[cookieName]; |
05 | if (null == authCookie) |
09 | FormsAuthenticationTicket authTicket = null; |
12 | authTicket = FormsAuthentication.Decrypt(authCookie.Value); |
18 | if (null == authTicket) |
22 | FormsIdentity id = new FormsIdentity(authTicket); |
23 | String[] roles = id.Ticket.UserData.Split('|'); |
24 | System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id, roles); |
25 | Context.User = principal; |
4. 在web.config文件中,允许登录文件的匿名访问,以便在未登录的情况下显示登录界面,注意:如果包含图片、css等文件,也需要设置这些资源允许匿名访问。
02 | <location path="AdminLogin.aspx"> |
09 | <location path="UserLogin.aspx"> |
17 | <authentication mode="Forms"> |
18 | <forms loginUrl="Login.aspx" path="/" protection="Encryption"></forms> |
5. 这样,当访问admin文件夹下的内容时,会直接转到AdminLogin.aspx界面。在登录之后,就可以在/Admin/文件夹下的页面中使用下面的方法得到当前登录的用户名和所具有的角色,根据角色来判断当前用户是否有权操作:
1 | Response.Write("<li>当前登录用户 = " + Page.User.Identity.Name); |
2 | Response.Write("<li>admin = " + Page.User.IsInRole("admin")); |
3 | Response.Write("<li>reader = " + Page.User.IsInRole("reader")); |
为了简单起见,可以写一个Admin使用的基类页面,统一在基类页面中进行权限的处理。