答案是肯定的,使用Forms身份验证可以通过角色的方式来达到上述目的。在本例中,我们可以先设定三种角色:admin、backup和 reader,其中,admin角色也就是管理员角色,该角色可以访问admin目录、backup目录和user目录;backup角色也就是系统备份 员角色,该角色不能访问admin目录,但可以访问backup目录和user目录;而reader角色是普通用户角色,该 角色不能访问admin目录、backup目录,只能访问user目录。
第一步、设置Forms身份验证方式和登录网页。
要达到上述目的,首先我们需要修改web.config文件,这次修改的目的是让网站拥有Forms身份验证功能(详见《asp.net中的窗体身份验证(最简单篇)》、《asp.net中的窗体身份验证(分目录验证篇)》和《asp.net中的窗体身份验证(完整篇之附录:web.config中相应节点详解)》),修改方式是: 在<system.web>和</system.web>中找到<authentication>节,将其改为 “<authentication mode="Forms"></authentication>”,其中Forms代表使用表单认证。
当然,你也可以在web.config文件中设置用户登录的网页,设置方法为:在<authentication mode="Forms">和</authentication>节点中添加“<forms loginUrl="~/login.aspx"></forms>”代码,其中loginUrl参数用于说明录页面,如果没有设置该 参数的话,默认的登录网页就是网站根目录的login.aspx网页。完整的代码如下所示:
- <authentication mode="Forms"><!--验证方式为窗体验证-->
- <forms loginUrl="~/login.aspx"></forms><!-- 登录网页为login.aspx-->
- </authentication>
第二步、设置默认的用户访问方式。
在本例中,我们要求是网站根目录中的网页可以让匿名用户访问,因此,在设置完Forms身份验证方式之后,我们还要设置默认的用户访问方式——也就是让 所有的匿名用户访问。设置方式是:在<system.web>和</system.web>节点中添加以下代码:
- <authorization>
- <allow users="?"/><!-- 默认为所有用户都能访问-->
- </authorization>
第三步、设置不同目录的访问方式。
在《asp.net中的窗体身份验证(分目录验证篇)》中,曾经介绍过分目录的验证方式,这种方式是在目录中创建一 个web.config文件,然后在web.config文件中的<system.web>和</system.web>小节 中,添加“<authorization></authorization>”节点,在该节点中设置不让匿名用户访问。在这里, 我们介绍另外一种设置不同目录的访问方式,假设,我们现在要指定admin目录不能让匿名用户访问,那么,我们可以在根目录的web.config文件 的<system.web>和</system.web>中添加以下代码:
- <location path="admin">
- <system.web>
- <authorization>
- <!--拒绝所有匿名用户访问-->
- <deny users="?"/>
- </authorization>
- </system.web>
- </location>
在以上代码中,<location>节点的path参数用于说明要对哪个目录可以访问控制,而<authorization> 节点的作用同样是用于设置具体的访问权限。在以上代码中,<deny>节点用于说明拒绝哪些用户访问,如果需要拒绝所有用户访问,那么就要使 用到“<deny users="*"/>”代码;如果需要允许所有用户访问,那么就需要使用“<allow users="*"/>”代码。然而,在<deny>节点和<allow>节点中还可以设置拒绝或允许哪些角色的用户访 问,设置方法为“<deny roles="admin"/>”或“<allow roles="admin"/>”。其中,roles参数用于说明指定的拒绝或允许访问的角色名,如果存在多个角色,则用逗号进行分割。在本例中, 要求admin目录只能让admin角色的用户访问、backup目录可以让admin角色和backup角色的用户访问,user目录可以让所有能过身 份验证的用户访问(无论是什么角色),可以使用以下代码进行设置:
- <!--设置admin目录的访问权 限-->
- <location path="admin">
- <system.web>
- <authorization>
- <!--允许角色为admin的用户可以访问-->
- <allow roles="admin"/>
- <!--拒绝所有其他用户访问-->
- <deny users="*"/>
- <!--注意以上两句的次序不能变-->
- </authorization>
- </system.web>
- </location>
- <!--设置backup目录的访问权限-->
- <location path="backup">
- <system.web>
- <authorization>
- <!--允许角色为admin和backup的用户可以访问-->
- <allow roles="admin,backup"/>
- <!--拒绝所有其他用户访问-->
- <deny users="*"/>
- <!--注意以上两句的次序不能变-->
- </authorization>
- </system.web>
- </location>
- <!--设置user目录的访问权限-->
- <location path="user">
- <system.web>
- <authorization>
- <!--拒绝所有匿名用户访问-->
- <deny users="?"/>
- </authorization>
- </system.web>
- </location>
第四步、添加登录页面。
由于在本例中,指定了网站目录中的login.aspx网页为登录面页,因此,在网站的根目录中添加一个login.aspx文件。然后在该文件中添加 一个文本框(用于输入用户名)和一个密码框,再添加一个确定按钮。为了方便起见,我们假设用户名和密码都为1时,登录用户的角色为admin;用户名和密 码都为2时,登录用户的角色为backup;用户名和密码都为3时,登录用户的角色为reader。登录网页中的代码如下所示:
- //用户 名
- string UserName = TextBox1.Text;
- //密码
- string UserPassword = TextBox2.Text;
- //用户角色
- string UserRole = "";
- //用户身份验证
- if ((UserName == "1" && UserPassword == "1") || (UserName == "2" && UserPassword == "2") || (UserName == "3" && UserPassword == "3"))
- {
- //判断用户权限
- switch (UserName)
- {
- case "1":
- UserRole = "admin";
- break;
- case "2":
- UserRole = "backup";
- break;
- case "3":
- UserRole = "reader";
- break;
- }
- //创建一个身份验证票
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "yundao", DateTime.Now, DateTime.Now.AddMinutes(30), false, UserRole);
- //将身份验证票加密
- string EncrTicket = FormsAuthentication.Encrypt(ticket);
- //创建一个Cookie
- HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName, EncrTicket);
- //将Cookie写入客户端
- Response.Cookies.Add(myCookie);
- //跳转到初始请求页或默认页面
- Response.Redirect(FormsAuthentication.GetRedirectUrl("yundao", false));
- }
以上代码和前面介绍过的身份验证代码类似,惟一不同的是将用户角色写在了身体验证票的用户数据(也就是 FormsAuthenticationTicket()方法的最后一个参数)中。
第五步、将用户角色添加到表示用户身份的GenericPrincipal对象中。
经过上面的步骤,我们已经将用户的角色存放在身份验证票据中了,然而,此时用于表示用户身份的GenericPrincipal对象中却还只是包含了用 户信息而不包含用户的角色信息。在这一步骤中,我们必须将用户角色信息存放到GenericPrincipal对象中。至于怎么样将用户角色信息存放到 GenericPrincipal对象中呢,我们就必须要先了解一下用户验证与授权的过程了:
1、客户端浏览器发送一个请求到服务器端。
2、服务器端接收到请求之后,会先检查该请求中是否包含有可能存在的恶意标记,如果存在恶意标记则会返回错误信息。
3、激活作为HTTP执行管线链中的第一个事件——BeginRequest事件。
4、处理完BeginRequest事件之后,asp.net将会创建用户标识,此时激发AuthenticateRequest事件,该事件可以确保 事件处理程序之前对请求进行身份验证。
5、在AuthenticateRequest 事件之后,会继续触发PostAuthenticateRequest 事件,该事件同时是在asp.net创建用户标识时触发,但在该事件中可以访问由AuthenticateRequest事件中处理的任何数据。
6、然后,asp.net会进入用户授权验证,此时会激发AuthorizeRequest事件。
7、验证授权完毕之后,当用户获得了授权时,将会激发PostAuthorizeRequest事件。
8、到第7步为止,整个用户身份验证就已经完毕了,接下来将会激发PostResolveRequestCache事件,在该事件之后还会有N个事件, 直到向客户端返回信息为止。但那件事件都不是本文的重点,也就不详细介绍了。
在用户登录确认身份之后,通常都会将身份验证票写到客户端的cookie中,然后客户端会将信息发送到服务器上,而在服务器端,asp.net会为每一 个HTTP请求都分配一个HttpApplication对象,该对象的作用是处理HTTP请求的,也就是用来处理以上步骤的。从以上步骤中可以看出,身 份验证是从AuthenticateRequest事件激发开始的,在该事件触发后,asp.net就会根据客户端发来的包含身份验证票的Cookie建 立用户身份。
而asp.net创建的用户身份包含在HttpContext.User属性中,可以使用“HttpContext.Current.User”来获得 得这个用户身份。对于Forms验证而言,HttpContext.User属性是一个GenericPrincipal对象,该对象有两个重要的属性: 第一个是Identity属性,该属性存放的是当前用户的标识。Identity属性就是一个FormsIdentity对象,该对象的Name属性可以 用户标识、Ticket属性可以获得用户的身份验证票。另外,GenericPrincipal对象还有一个m_role属性,这个属性是 GenericPrincipal对象的私有属性,所以不能通过GenericPrincipal.m_role来访问,该属性是一个数组,用于存放用户 角色。如果想知道用户是否属于某个角色,可以通过GenericPrincipal 对象的IsInRole()方法来判断。然而,由于身份验证票中并没有提供m_role参数,因此,在asp.net创建用户身份时,用户是没有角色信息 的。换句话而言,对于Forms身份验证而言,在默认情况下,GenericPrincipal对象的m_role属性为空。
因此,如果想要使用角色的用户身份验证方式,就必须要在代表用户身份GenericPrincipal对象中添加用户角色。而且,这个工作必须要在第3 个步骤中完成,因为第4个步骤的作用就是创建用户标识。因此,我们必须要在网站的根目录中创建一个Global.asax文件,Global.asax文 件的作用就不用我再多说了了吧?
在Global.asax文件中添加代码“protected void Application_AuthenticateRequest(object sender, EventArgs e)”用于处理AuthenticateRequest事件。处理步骤如下所示:
1、首先判断用户的身份验证信息是否为空,如果HTTP请求中就没有用户身份验证信息,那么后面的步骤也就没必须进行了。前面介绍过,asp.net创 建的用户身份包含在HttpContext.User属性中,因此,在该步骤中只需要判断HttpContext.User属性是否为空即可达到目的。
2、然后判断用户是否已经通过了身份验证,如果没有通过身份验证,那么在访问受限网页时,asp.net会自动跳转到登录网页上。前面介绍 过,HttpContext.User属性也就是一个GenericPrincipal对象,该对象的Identity属性值为用户标识,那么只要判断 Identity.IsAuthenticated是否为true,就可以知道用户是否已经通过身份验证。
3、由于asp.net的身份验证方式有多种,因此,在这里我们还要判断一下用户通过的身份验证方式是否为Forms验证方式。要达到该目的,只需要判 断User.Identity是否为FormsIdentity对象即可。
4、然后,可以获得已经通过了Forms身份验证的用户标识,再从该用户标识的身份验证票中获得用户数据(也就是包含了用户角色的字符串)。
5、再然后,将用户角色字符串放到一个角色数组中——如果一个用户属于多个角色,通常我们都会将不同的角色使用逗号进行分割后放在用户数据中,然后将第 4步骤中获得的用户数据分割后放在一个数组中,这样就得到了一个角色数组。
6、最后,创建一个新的GenericPrincipal对象,在创建该对象时将从第4步骤中获得的用户标识以及第5步骤中获得的角色数组一起放入 GenericPrincipal对象,然后将该对象赋值给HttpContext.User。
这样,用户身份中就包含了角色信息。在随后的访问用,asp.net就会自动验证用户的角色以判断该用户是否可以访问限制的目录了。具体代码如下所示:
- protected void Application_AuthenticateRequest(object sender, EventArgs e)
- {
- //判断正在请求页的用户的身份验证信息是否为空
- if (HttpContext.Current.User != null)
- {
- //判断用户是否已经进行了身份验证
- if (HttpContext.Current.User.Identity.IsAuthenticated)
- {
- //判断当前用户身份验证的方式是否为Forms身份验证方式
- if (HttpContext.Current.User.Identity is FormsIdentity)
- {
- //获得进行了Forms身份验证的用户标识
- FormsIdentity UserIdent = (FormsIdentity)(HttpContext.Current.User.Identity);
- //从身份验证票中获得用户数据
- string UserData = UserIdent.Ticket.UserData;
- //分割用户数据得到用户角色数组
- string[] rolues = UserData.Split(',');
- //从用户标识和角色组初始化GenericPrincipal类
- HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(UserIdent, rolues);
- }
- }
- }
- }
至此,一个完整的带角色控制的窗体身份验证就已经做完了。
=============================
原创不容易,转载请注明出处。
相关链接:
3、asp.net中的窗体身份验证 (完整篇之一:创建asp.net的窗体身份验证方式)
4、asp.net中的窗体身份验证 (完整篇之二:asp.net的窗体身份验证过程)
5、asp.net中的窗体身份验证 (完整篇之三:用户登录页面)
6、asp.net中的窗体身份验证 (完整篇之四:获得用户数据)
7、asp.net中的窗体身份验证 (完整篇之五:退出登录)
8、asp.net中的窗体身份验证 (完整篇之附录:web.config中相应节点详解)
9、asp.net中的窗体身份验证 (不同的角色访问不同的目录)
11、asp.net中的窗体身份验证 (完整篇之六:登出后不能通过后退回来面来的页面)
有兴趣的朋友可以在http://www.aspxfans.com /myBlogFile/窗体身份验证:让不同角色的用户进入不同的目录.rar或http://download.csdn.net/source/1967436下 载本例的源代码。