SAL

  博客园  :: 首页  :: 新随笔  :: 订阅 订阅  :: 管理

asp.net中使用基于角色role的Forms验证

Posted on 2009-11-12 20:08  SAL  阅读(228)  评论(0编辑  收藏  举报

asp.net中使用基于角色role的Forms验证,大致经过几下四步:
1.配置系统web.config

<system.web> 
<authentication mode="Forms" > 
 
<forms name=".yaoCookies" loginUrl="/duan/Manage/login.aspx" protection="All"
  timeout
="20" path="/" />
 
</authentication>
</system.web>

其中<authentication mode= "forms"> 表示本应用程序采用Forms验证方式。
1). <forms>标签中的name表示指定要用于身份验证的 HTTP Cookie。默认情况下,name 的值是 .ASPXAUTH。采用此种方式验证用户后,以此用户的信息建立一个FormsAuthenticationTicket类型的身份验证票,再加密序列化为一个字符串,最后将这个字符串写到客户端的name指定名字的Cookie中.一旦这个Cookie写到客户端后,此用户再次访问这个web应用时会将连同Cookie一起发送到服务端,服务端将会知道此用户是已经验证过的.

2). <forms>标签中的loginUrl指定如果没有找到任何有效的身份验证 Cookie,为登录将请求重定向到的 URL。默认值为 default.aspx。loginUrl指定的页面就是用来验证用户身份的,一般此页面提供用户输入用户名和密码,用户提交后由程序来根据自己的需要来验证用户的合法性(大多情况是将用户输入信息同数据库中的用户表进行比较),如果验证用户有效,则生成同此用户对应的身份验证票,写到客户端的Cookie,最后将浏览器重定向到用户初试请求的页面.一般是用FormsAuthentication.RedirectFromLoginPage 方法来完成生成身份验证票,写回客户端,浏览器重定向等一系列的动作.

public static void RedirectFromLoginPage( string userName, bool createPersistentCookie, string strCookiePath );

其中:
userName: 就是此用户的标示,用来标志此用户的唯一标示,不一定要映射到用户账户名称.
createPersistentCookie: 标示是否发出持久的 Cookie。
若不是持久Cookie,Cookie的有效期Expiration属性有当前时间加上web.config中timeout的时间,每次请求页面时,在验证身份过程中,会判断是否过了有效期的一半,要是的话更新一次cookie的有效期;若是持久cookie,Expiration属性无意义,这时身份验证票的有效期有cookie的Expires决定,RedirectFromLoginPage方法给Expires属性设定的是50年有效期。
strCookiePath: 标示将生成的Cookie的写到客户端的路径,身份验证票中保存这个路径是在刷新身份验证票Cookie时使用(这也是生成Cookie的Path),若没有strCookiePath 参数,则使用web.config中 path属性的设置。

这里可以看到,此方法参数只有三个,而身份验证票的属性有七个,不足的四个参数是这么来的:
IssueDate:Cookie发出时间由当前时间得出,
Expiration:过期时间由当前时间和<forms>标签中的timeout参数算出。此参数对非持久性cookie有意义。
UserData:这个属性可以用应用程序写入一些用户定义的数据,此方法没有用到这个属性,只是简单的将此属性置为空字符串,请注意此属性,在后面我们将要使用到这个属性。
Version: 版本号由系统自动提供。

RedirectFromLoginPage方法生成生成身份验证票后,会调用FormsAuthentication.Encrypt 方法,将身份验证票加密为字符串,这个字符串将会是以.ASPXAUTH为名字的一个Cookie的值。
这个Cookie的其它属性的生成:
Domain,Path属性为确省值,Expires视createPersistentCookie参数而定,若是持久cookie,Expires设为50年以后过期;若是非持久cookie,Expires属性不设置。
生成身份验证Cookie后,将此Cookie加入到Response.Cookies中,等待发送到客户端。
最后RedirectFromLoginPage方法调用FormsAuthentication.GetRedirectUrl 方法获取到用户原先请求的页面,重定向到这个页面。

3). <forms>标签中的timeout和path,是提供了身份验证票写入到Cookie过期时间和默认路径。

以上就是基于Forms身份验证的过程,它完成了对用户身份的确认。

2.在受保护的文件夹如Manage下创建一web.config文件,内容如

<configuration>
  <!--指定对整个Manage目录的访问权限-->
  
<system.web>
    
<authorization>
           <!--多个角色用,分隔-->
          
<allow roles="admin,user"/>
           
<deny users="*" />
      
</authorization>
  
</system.web>

  
<!--也可控制某个页的权限

  <location path="AnnounceList.aspx">
     <system.web>
        <authorization>
           <allow roles="admin"/>
           <deny users="*" />
        </authorization>
     </system.web>
  </location>

  <location path="ConfigInfo.aspx">
     <system.web>
        <authorization>
           <allow roles="users"/>
           <deny users="*" />
        </authorization>
     </system.web>
  </location>

  
-->
</configuration>

注:此配置内容也可以加入到系统的web.config文件中,注意加入位置:

........
    </system.web>

    <location path="Manage/AnnounceList.aspx">
    
<system.web>
     
<authorization>
      
<allow roles="admin"/>
      
<deny users="*" />
     
</authorization>
     
</system.web>
    </location>

</configuration>

<allow>标签表示允许访问,其中的属性
1). users:一个逗号分隔的用户名列表,这些用户名已被授予对资源的访问权限。问号 (?) 允许匿名用户;星号 (*) 允许所有用户。
2). roles:一个逗号分隔的角色列表,这些角色已被授予对资源的访问权限。
3). verbs:一个逗号分隔的 HTTP 传输方法列表,这些 HTTP 传输方法已被授予对资源的访问权限。注册到 ASP.NET 的谓词为 GET、HEAD、POST 和 DEBUG。

<deny>标签表示不允许访问。其中的属性同上面的。

在运行时,授权模块迭代通过 <allow> 和 <deny> 标记,直到它找到适合特定用户的第一个访问规则。然后,它根据找到的第一项访问规则是 <allow> 还是 <deny> 规则来允许或拒绝对 URL 资源的访问。Machine.config 文件中的默认身份验证规则是 <allow users="*"/>,因此除非另行配置,否则在默认情况下会允许访问。

那么这些user 和roles又是如何得到的呢?下面看一下授权的详细过程:

1). 一旦一个用户访问这个网站,就行登录确认了身份,身份验证票的cookie也写到了客户端。之后,这个用户再次申请这个web的页面,身份验证票的cookie就会发送到服务端。在服务端,asp.net为每一个http请求都分配一个HttpApplication对象来处理这个请求,在HttpApplication.AuthenticateRequest事件后,安全模块已建立用户标识,就是此用户的身份在web端已经建立起来,这个身份完全是由客户端发送回来的身份验证票的cookie建立的。
2). 用户身份在HttpContext.User 属性中,在页面中可以通过Page.Context 来获取同这个页面相关的HttpContext对象。对于Forms验证,HttpContext.User属性是一个GenericPrincipal类型的对象,GenericPrincipal只有一个公开的属性Identity,有个私有的m_role属性,是string[]类型,存放此用户是属于哪些role的数组,还有一个公开的方法IsInRole(string role),来判断此用户是否属于某个角色。
由于身份验证票的cookie中根本没有提供role这个属性,就是说Forms身份验证票没有提供此用户的role信息,所以,对于Forms验证,在服务端得到的GenericPrincipal 用户对象的m_role属性永远是空的。
3). GenericPrincipal. Identity 属性是一个FormsIdentity类型的对象,这个对象有个Name属性,就是此用户的标示,访问授权就是将此属性做为user来进行授权验证的。FormsIdentity还有一个属性,就是Ticket属性,此属性是身份验证票FormsAuthenticationTicket类型,就是之前服务器写到客户端的身份验证票。
服务器在获取到身份验证票FormsAuthenticationTicket对象后,查看这个身份验证票是不是非持久的身份验证,是的话要根据web.config中timeout属性设置的有效期来更新这个身份验证票的cookie(为避免危及性能,在经过了超过一半的指定时间后更新该 Cookie。这可能导致精确性上的损失。持久性 Cookie 不超时。)
4). 在HttpApplication.ResolveRequestCache事件之前,asp.net开始取得用户请求的页面,建立HttpHandler控制点。这就意味着,在HttpApplication.ResolveRequestCache事件要对用户访问权限就行验证,看此用户或角色是否有权限访问这个页面,之后在这个请求的生命周期内再改变此用户的身份或角色就没有意义了。

以上是Forms验证的全过程,可以看出,这个Forms验证是基于用户的,没有为角色的验证提供直接支持。身份验证票FormsAuthenticationTicket 中的Name属性是用户标示,其实还有一个属性UserData,这个属性可以由应用程序来写入自定义的一些数据,我们可以利用这个字段来存放role的信息,从而达到基于角色验证的目的。

3.登录页

//登录按钮
private void Button1_Click(object sender, System.EventArgs e)
{
            //实体类AdminUserVO对应AdminUser用户表。
            AdminUserVO adminUserVO 
= new AdminUserVO();

            adminUserVO.Uname 
= UserName.Text.Trim();
            adminUserVO.Upwd 
= UserPwd.Text.Trim();
            adminUserVO.LastIP 
= HttpContext.Current.Request.UserHostAddress;
            adminUserVO.LastTime 
= DateTime.Now;

            
bool flag = (new LoginDAO()).Chk(adminUserVO);

            
if (flag)
            {
                
//非角色验证时可以用这句:
                
//System.Web.Security.FormsAuthentication.SetAuthCookie(UserName.Text.Trim(),false);

                
//创建角色验证信息,把role信息写入到UserData中
                SetLoginCookie(adminUserVO,adminUserVO.Roles.ToLower());

                HttpContext.Current.Response.Redirect(
"Main.aspx");
            }
            
else
            {
                HttpContext.Current.Response.Write(
"登录失败");
            }
}

 

//SetLoginCookie方法
public static void SetLoginCookie(AdminUserVO u, string roles)
  {
   
//建立身份验证票对象
   FormsAuthenticationTicket ticket = new FormsAuthenticationTicket (1,u.Uname, DateTime.Now, DateTime.Now.AddMinutes(30), false,roles,"/");
   
//加密序列化验证票为字符串
   string hashTicket = FormsAuthentication.Encrypt (ticket) ;
   HttpCookie userCookie 
= new HttpCookie(FormsAuthentication.FormsCookieName, hashTicket);
   HttpContext.Current.Response.Cookies.Add(userCookie);
  }

FormsAuthenticationTicket参数说明:
FormsAuthenticationTicket(
int version, //设为1,版本号由系统自动提供
string name, //用户标示,获取与身份验证 Cookie 关联的用户名
DateTime issueDate, //Cookie 的发出时间, 设置为 DateTime.Now
DateTime expiration, //获取 Cookie 过期的日期/时间
bool isPersistent, //是否持久性(根据需要设置,若是设置为持久性,在发出cookie时,cookie的Expires设置一定要设置),如果已发出持久的 Cookie,则返回 true。否则,身份验证 Cookie 将限制在浏览器生命周期范围内。
string userData, //获取存储在 Cookie 中的应用程序定义字符串,这里用上面准备好的用逗号分割的role字符串
string cookiePath // 返回发出 Cookie 的路径。注意,窗体的路径设置为"/",这要同发出cookie的路径一致,因为刷新cookie要用这个路径。由于窗体区分大小写,这是为了防止站点中的 URL 的大小写不一致而采取的一种保护措施。
);

4.Global.asax.cs

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
  {
   HttpApplication app 
= (HttpApplication) sender;  
   HttpContext ctx 
= app.Context ; //获取本次Http请求的HttpContext对象  
   if (ctx.User != null)
   {
    
if (ctx.Request.IsAuthenticated == true//验证过的一般用户才能进行角色验证  
    {  
     System.Web.Security.FormsIdentity fi 
= (System.Web.Security.FormsIdentity)ctx.User.Identity ;  
     System.Web.Security.FormsAuthenticationTicket ticket 
= fi.Ticket ; //取得身份验证票  
     string userData = ticket.UserData;//从UserData中恢复role信息
     
string[] roles = userData.Split (',') ; //将角色数据转成字符串数组,得到相关的角色信息  
     ctx.User 
= new System.Security.Principal.GenericPrincipal (fi, roles) ; //这样当前用户就拥有角色信息了
    } 
   }
  }

注:如果使用HttpModule的话,此处代码应该加入在AuthenticateRequest事件中。