asp.net中的窗体身份验证(不同的角色访问不同的目录)

 在《asp.net中的窗体身份验证(最简单篇)》说明了如何让通过了身份验证的用户访问网站,但是该方法中有一个缺点,就是访问整个网站都需要用户身份验证。因此,在《asp.net中的窗体身份验证(分目录验证篇)》中介绍了如何让通过了身份验证的用户访问特定的目录,例如网站根目录中的网页是任何用户都能访问的,而admin目录则只能让通过身份验证的用户访问。这种方法可以解决大部分网站的身份控制,但是,对于一些复杂的网站而言,这种方式就有点捉袊见肘了。例如,有一个网站,我们要求根目录中的网页可以让任何用户浏览,而admin目录中的网页只有管理员才能访问、backup目录中的网页只有管理员和系统备份员才能访问、user目录中的网页除了匿名用户之外的所有用户都能访问,对于这种情况,那要怎么办呢?是不是还可以使用Forms身份验证呢?

    答案是肯定的,使用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网页。完整的代码如下所示:

  1. <authentication mode="Forms"><!--验证方式为窗体验证-->  
  2.     <forms loginUrl="~/login.aspx"></forms><!--登录网页为login.aspx-->  
  3. </authentication>  

 

 

 

    第二步、设置默认的用户访问方式。

    在本例中,我们要求是网站根目录中的网页可以让匿名用户访问,因此,在设置完Forms身份验证方式之后,我们还要设置默认的用户访问方式——也就是让所有的匿名用户访问。设置方式是:在<system.web>和</system.web>节点中添加以下代码:

  1. <authorization>  
  2.     <allow users="?"/><!--默认为所有用户都能访问-->  
  3. </authorization>  

 

 

 

    第三步、设置不同目录的访问方式。

    在《asp.net中的窗体身份验证(分目录验证篇)》中,曾经介绍过分目录的验证方式,这种方式是在目录中创建一个web.config文件,然后在web.config文件中的<system.web>和</system.web>小节中,添加“<authorization></authorization>”节点,在该节点中设置不让匿名用户访问。在这里,我们介绍另外一种设置不同目录的访问方式,假设,我们现在要指定admin目录不能让匿名用户访问,那么,我们可以在根目录的web.config文件的<system.web>和</system.web>中添加以下代码:

  1. <location path="admin">  
  2.     <system.web>  
  3.         <authorization>  
  4.                 <!--拒绝所有匿名用户访问-->  
  5.                 <deny users="?"/>  
  6.         </authorization>  
  7.     </system.web>  
  8. </location>  

 

 

    在以上代码中,<location>节点的path参数用于说明要对哪个目录可以访问控制,而<authorization>节点的作用同样是用于设置具体的访问权限。在以上代码中,<deny>节点用于说明拒绝哪些用户访问,如果需要拒绝所有用户访问,那么就要使用到“<deny users="*"/>”代码;如果需要允许所有用户访问,那么就需要使用“<allow users="*"/>”代码。然而,在<deny>节点和<allow>节点中还可以设置拒绝或允许哪些角色的用户访问,设置方法为“<deny roles="admin"/>”或“<allow roles="admin"/>”。其中,roles参数用于说明指定的拒绝或允许访问的角色名,如果存在多个角色,则用逗号进行分割。在本例中,要求admin目录只能让admin角色的用户访问、backup目录可以让admin角色和backup角色的用户访问,user目录可以让所有能过身份验证的用户访问(无论是什么角色),可以使用以下代码进行设置:

  1. <!--设置admin目录的访问权限-->  
  2. <location path="admin">  
  3.     <system.web>  
  4.         <authorization>  
  5.             <!--允许角色为admin的用户可以访问-->  
  6.             <allow roles="admin"/>  
  7.             <!--拒绝所有其他用户访问-->  
  8.             <deny users="*"/>  
  9.             <!--注意以上两句的次序不能变-->  
  10.         </authorization>  
  11.     </system.web>  
  12.  </location>  
  13. <!--设置backup目录的访问权限-->  
  14. <location path="backup">  
  15.     <system.web>  
  16.         <authorization>  
  17.             <!--允许角色为admin和backup的用户可以访问-->  
  18.             <allow roles="admin,backup"/>  
  19.             <!--拒绝所有其他用户访问-->  
  20.             <deny users="*"/>  
  21.             <!--注意以上两句的次序不能变-->  
  22.         </authorization>  
  23.     </system.web>  
  24. </location>  
  25. <!--设置user目录的访问权限-->  
  26. <location path="user">  
  27.     <system.web>  
  28.         <authorization>  
  29.             <!--拒绝所有匿名用户访问-->  
  30.             <deny users="?"/>  
  31.         </authorization>  
  32.     </system.web>  
  33. </location>  

 

 

 

    第四步、添加登录页面。

    由于在本例中,指定了网站目录中的login.aspx网页为登录面页,因此,在网站的根目录中添加一个login.aspx文件。然后在该文件中添加一个文本框(用于输入用户名)和一个密码框,再添加一个确定按钮。为了方便起见,我们假设用户名和密码都为1时,登录用户的角色为admin;用户名和密码都为2时,登录用户的角色为backup;用户名和密码都为3时,登录用户的角色为reader。登录网页中的代码如下所示:

  1. //用户名   
  2. string UserName = TextBox1.Text;  
  3. //密码   
  4. string UserPassword = TextBox2.Text;  
  5. //用户角色   
  6. string UserRole = "";  
  7. //用户身份验证   
  8. if ((UserName == "1" && UserPassword == "1") || (UserName == "2" && UserPassword == "2") || (UserName == "3" && UserPassword == "3"))  
  9. {  
  10.     //判断用户权限   
  11.     switch (UserName)  
  12.     {   
  13.         case "1":  
  14.             UserRole = "admin";  
  15.             break;  
  16.         case "2":  
  17.             UserRole = "backup";  
  18.             break;  
  19.         case "3":  
  20.             UserRole = "reader";  
  21.             break;  
  22.     }  
  23.      
  24.     //创建一个身份验证票   
  25.     FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "yundao", DateTime.Now, DateTime.Now.AddMinutes(30), false, UserRole);  
  26.     //将身份验证票加密   
  27.     string EncrTicket = FormsAuthentication.Encrypt(ticket);  
  28.     //创建一个Cookie   
  29.     HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName, EncrTicket);  
  30.     //将Cookie写入客户端   
  31.     Response.Cookies.Add(myCookie);  
  32.     //跳转到初始请求页或默认页面   
  33.     Response.Redirect(FormsAuthentication.GetRedirectUrl("yundao"false));  
  34. }  

 

 

    以上代码和前面介绍过的身份验证代码类似,惟一不同的是将用户角色写在了身体验证票的用户数据(也就是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就会自动验证用户的角色以判断该用户是否可以访问限制的目录了。具体代码如下所示:

  1. protected void Application_AuthenticateRequest(object sender, EventArgs e)  
  2. {  
  3.     //判断正在请求页的用户的身份验证信息是否为空   
  4.     if (HttpContext.Current.User != null)  
  5.     {  
  6.         //判断用户是否已经进行了身份验证   
  7.         if (HttpContext.Current.User.Identity.IsAuthenticated)  
  8.         {  
  9.             //判断当前用户身份验证的方式是否为Forms身份验证方式   
  10.             if (HttpContext.Current.User.Identity is FormsIdentity)  
  11.             {  
  12.                 //获得进行了Forms身份验证的用户标识   
  13.                 FormsIdentity UserIdent = (FormsIdentity)(HttpContext.Current.User.Identity);  
  14.                 //从身份验证票中获得用户数据   
  15.                 string UserData = UserIdent.Ticket.UserData;  
  16.                 //分割用户数据得到用户角色数组   
  17.                 string[] rolues = UserData.Split(',');  
  18.                 //从用户标识和角色组初始化GenericPrincipal类   
  19.                 HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(UserIdent, rolues);  
  20.             }  
  21.         }  
  22.     }  
  23. }  

 

 

 

    至此,一个完整的带角色控制的窗体身份验证就已经做完了。

posted @ 2012-05-30 21:13  齐鲁青未了  阅读(335)  评论(0编辑  收藏  举报