Asp.net中基于Forms验证的角色验证授权

来源:互联网 发布:时时彩软件平刷王 编辑:程序博客网 时间:2024/05/16 14:07

原文: http://blog.csdn.net/goodshot/article/details/5942444


1. web.config配置好form authentication和authorize的信息

2. 第一次请求生成用户验证信息票,存到cookie里去,role信息存储到userdata里, 关键结构 FormsAuthenticationTicket,FormsAuthentication.RedirectFromLoginPage

3. 后续用户请求,在Application_AuthenticateRequest中把userdata里的信息添加到role信息里,Ctx.User = new GenericPrincipal (Id, Roles)

4. signout时调用 FormsAuthentication.SignOut(); 


Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活。

  Forms 验证方式对基于用户的验证授权提供了很好的支持,可以通过一个登录页面验证用户的身份,将此用户的身份发回到客户端的Cookie,之后此用户再访问这个web应用就会连同这个身份Cookie一起发送到服务端。服务端上的授权设置就可以根据不同目录对不同用户的访问授权进行控制了。

  问题来了,在实际是用中我们往往需要的是基于角色,或者说基于用户组的验证和授权。对一个网站来说,一般的验证授权的模式应该是这样的:根据实际需求把用户分成不同的身份,就是角色,或者说是用户组,验证过程不但要验证这个用户本身的身份,还要验证它是属于哪个角色的。而访问授权是根据角色来设置的,某些角色可以访问哪些资源,不可以访问哪些资源等等。要是基于用户来授权访问将会是个很不实际的做法,用户有很多,还可能随时的增减,不可能在配置文件中随时的为不断增加的新用户去增加访问授权的。

  下面大概的看一下Forms的过程。

  Forms身份验证基本原理:

  一 身份验证

  要采用Forms身份验证,先要在应用程序根目录中的Web.config中做相应的设置:

<authentication mode="forms"> 
<forms name=".ASPXAUTH " loginUrl="/login.aspx" timeout="30" path= "/">
</forms> 
</authentication>

  其中<authentication mode= "forms"> 表示本应用程序采用Forms验证方式。

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

  再看一下身份验证票都包含哪些信息呢,我们看一下FormsAuthenticationTicket类:
CookiePath: 返回发出 Cookie 的路径。注意,窗体的路径设置为 /。由于窗体区分大小写,这是为了防止站点中的 URL 的大小写不一致而采取的一种保护措施。这在刷新 Cookie 时使用

Expiration: 获取 Cookie 过期的日期/时间。
IsPersistent: 如果已发出持久的 Cookie,则返回 true。否则,身份验证 Cookie 将限制在浏览器生命周期范围内。
IssueDate: 获取最初发出 Cookie 的日期/时间。
Name: 获取与身份验证 Cookie 关联的用户名。
UserData :获取存储在 Cookie 中的应用程序定义字符串。
Version: 返回字节版本号供将来使用。

  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身份验证的过程,它完成了对用户身份的确认。下面介绍基于Forms身份验证的访问授权。

  二 访问授权

  验证了身份,是要使用这个身份,根据不同的身份我们可以进行不同的操作,处理,最常见的就是对不同的身份进行不同的授权,Forms验证就提供这样的功能。Forms授权是基于目录的,可以针对某个目录来设置访问权限,比如,这些用户可以访问这个目录,那些用户不能访问这个目录。

  同样,授权设置是在你要控制的那个目录下的web.config文件中来设置:

<authorization>
<allow users="comma-separated list of users"
roles="comma-separated list of roles"
verbs="comma-separated list of verbs" />
<deny users="comma-separated list of users"
roles="comma-separated list of roles"
verbs="comma-separated list of verbs" />
</authorization>

<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的信息,从而达到基于角色验证的目的。

  Forms身份验证基于角色的授权

  一 身份验证

  在web.config的<authentication>的设置还是一样:

<authentication mode="forms"> 
<forms name=".ASPXAUTH " loginUrl="/login.aspx" timeout="30" path= "/">
</forms> 
</authentication>

  /login.aspx验证用户合法性页面中,在验证了用户的合法性后,还要有个取得此用户属于哪些role的过程,这个看各个应用的本身如何设计的了,一般是在数据库中会有个use_role表,可以从数据库中获得此用户属于哪些role,在此不深究如何去获取用户对应的role,最后肯定能够获得的此用户对应的所有的role用逗号分割的一个字符串。

  在上面的非基于角色的方法中,我们用了FormsAuthentication.RedirectFromLoginPage 方法来完成生成身份验证票,写回客户端,浏览器重定向等一系列的动作。这个方法会用一些确省的设置来完成一系列的动作,在基于角色的验证中我们不能用这一个方法来实现,要分步的做,以便将一些定制的设置加进来:

  1. 首先要根据用户标示,和用户属于的角色的字符串来创建身份验证票

public FormsAuthenticationTicket(
int version, //设为1
string name, //用户标示
DateTime issueDate, //Cookie 的发出时间, 设置为 DateTime.Now 
DateTime expiration, //过期时间
bool isPersistent, //是否持久性(根据需要设置,若是设置为持久性,在发出
cookie时,cookie的Expires设置一定要设置)
string userData, //这里用上面准备好的用逗号分割的role字符串
string cookiePath // 设为"/",这要同发出cookie的路径一致,因为刷新cookie
要用这个路径
); 

FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket (1,"kent",DateTime.Now, DateTime.Now.AddMinutes(30), false,UserRoles,"/") ;

  2. 生成身份验证票的Cookie

  2.1 将身份验证票加密序列化成一个字符串

string HashTicket = FormsAuthentication.Encrypt (Ticket) ; 

  2.2 生成cookie

HttpCookie UserCookie = new HttpCookie(FormsAuthentication.FormsCookieName, HashTicket) ;

  FormsAuthentication.FormsCookieName 是用来获取web.config中设置的身份验证cookie的名字,缺省为" .ASPXAUTH".

  若身份验证票中的isPersistent属性设置为持久类,则这个cookie的Expires属性一定要设置,这样这个cookie才会被做为持久cookie保存到客户端的cookie文件中.

  3. 将身份验证票Cookie输出到客户端
通过Response.Cookies.Add(UserCookie) 将身份验证票Cookie附加到输出的cookie集合中,发送到客户端.

  4. 重定向到用户申请的初试页面.

  验证部分代码(这部分代码是在login.aspx页面上点击了登录按钮事件处理代码):

private void Buttonlogin_Click(object sender, System.EventArgs e)
{
string user = TextBoxUser.Text; //读取用户名
string password = TextBoxPassword.Text; //读取密码
if(Confirm(user,password) == true) //confirm方法用来验证用户合法性的
{
string userRoles = UserToRole(user); //调用UserToRole方法来获取role字符串
FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket (1,user,DateTime.Now, DateTime.Now.AddMinutes(30), false,userRoles,"/") ; //建立身份验证票对象
string HashTicket = FormsAuthentication.Encrypt (Ticket) ; //加密序列化验证票为字符串
HttpCookie UserCookie = new HttpCookie(FormsAuthentication.FormsCookieName, HashTicket) ;
//生成Cookie
Context.Response.Cookies.Add (UserCookie) ; //输出Cookie
Context.Response.Redirect (Context.Request["ReturnUrl"]) ; // 重定向到用户申请的初始页面
}
else
{
// 用户身份未被确认时的代码
}
}
//此方法用来验证用户合法性的
private bool Confirm(string user,string password)
{
//相应的代码
}
//此方法用来获得的用户对应的所有的role用逗号分割的一个字符串
private string UserToRole(string user)
{
//相应的代码
}

  二 基于角色访问授权

  这里我们要做的是,将客户端保存的身份验证票中UserData中保存的表示角色的信息恢复到在服务端表示用户身份的GenericPrincipal对象中。(记住,原来的验证过程中, GenericPrincipal对象只包含了用户信息,没有包含role信息)

  一个Http请求的过程中,HttpApplication.AuthenticateRequest事件表示安全模块已建立用户标识,就是此用户的身份在web端已经建立起来, 在这个事件之后我们就可以获取用户身份信息了。

  在HttpApplication.ResolveRequestCache事件之前,asp.net开始取得用户请求的页面,建立HttpHandler控制点,这时就已经要验证用户的权限了,所以恢复用户角色的工作只能在HttpApplication.AuthenticateRequest事件和HttpApplication.ResolveRequestCache事件之间的过程中做.

  我们选择Application_AuthorizeRequest事件中做这个工作,可以在global.asax文件中处理HttpApplication的所有的事件,代码如下:

protected void Application_AuthorizeRequest(object sender, System.EventArgs e)
{
HttpApplication App = (HttpApplication) sender; 
HttpContext Ctx = App.Context ; //获取本次Http请求相关的HttpContext对象
if (Ctx.Request.IsAuthenticated == true) //验证过的用户才进行role的处理
{
FormsIdentity Id = (FormsIdentity)Ctx.User.Identity ; 
FormsAuthenticationTicket Ticket = Id.Ticket ; //取得身份验证票
string[] Roles = Ticket.UserData.Split (',') ; //将身份验证票中的role数据转成字符串数组
Ctx.User = new GenericPrincipal (Id, Roles) ; //将原有的Identity加上角色信息新建一个GenericPrincipal表示当前用户,这样当前用户就拥有了role信息
}
}

 

三.另一篇完整内容

1.用户的身份验证

  用户的三种验证方式:Windows、Forms、Passport
                      Windows是默认的身份验证方式,它是根据机器的访问权限来判断的
                      Passport是微软提供的验证方式
                      Forms就是表单验证,提供了以身份ID和密码的形式进行验证和授权管理功能


2.认证用户的原理
  用户登录的时候给用户一个表明身份的票据,以后用户登录的时候通过这个票据就能知道这个用户已经被注册


3.Forms验证设置的流程

  a.配置web.config启用Froms验证

    <authentication mode="Forms">
<forms defaultUrl="Default.aspx" loginUrl="Login.aspx"></forms>
    </authentication>   

  b.配置授权设置

    <authorization>        
        <allow users="*"/> //允许所有用户,我们也可以设置特定的ID用户访问
        <deny users="?"/>  //拒绝匿名用户
    </authorization>
  
    注意:
         先写allow,在写deny,否则会出现问题
        
  c.登陆页面中生成用户票据便于其他页面访问
  
    FormsAuthentication.SetAuthCookie(userid, true);  //为用户创建一个票证,并将其放入cookie或者url中


4.文件夹内授权设置

  a.在文件夹内添加一个web.config配置文件

  b.在里面配置授权设置


5.用户角色授权

   a.生成票据:

     string userId="admin";
     string pass="123";
     string roles="Adminstrator"; //从其他地方取得用户角色数据
     FormAuthenticationTicket Ticket=new FormAuthentication(1,userId,DateTime.Now,DataTime.Now.AddMinutes(30),true,roles); //建立身份验证票对象
     string HashTicket=FormsAuthentication.Encrypt(Ticket); //加密序列化验证为字符串
     HttpCookie UserCookie=new HttpCookie(FormAuthentication.FormsCookieName,HashTicket); //生成Cookie
     Context.Response.Cookies.Add(UserCookie); //票据写入Cookie
     Response.Redirect("Info.aspx");


   b. 给用户票据的时候在里面加了一个字符串的角色信息,比如“Administrator”,当一个请求过来的时候asp.NET会有一个Application_AuthenticateRequest的事件,专门用于用户认证授权,在         这个事件中我们只需要将这个字符表达的角色重建给用户就可以,我们在Global.asax的Application_AuthenticateRequest方法中增加如下代码:

     protected void Application_AuthenticateRequest(object sender,EventArgs e)
     {
       HttpApplication app=(HttpApplication)sender;
       HttpContext context=app.Context; //获取本次Http请求的HttpContext对象
       if(context.Request.IsAuthenticated) //验证过的一般用户才能进行角色验证
       {
          FormsIdentity Id=(FormsIdentity)context.User.Identity; //当前用户标识
          FormsAuthenticationTicket Ticket=Id.Ticket; //取得身份证票
          string[] Roles=Ticket.UserData.Split(','); //将角色数据专程字符串数组,得到相关的角色信息
          context.User=new System.Security.Principal.GenericPrincipal(Id,Roles); //重新生成带有角色信息的用户
       }
     }
     }


   c.更改配置文件,设置info.aspx页面只有Adminstrator的角色才可以访问,代码如下:

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

 

四、再一篇(asp.Net Forms身份验证和基于角色的权限访问)

主要思想:Forms身份验证用来判断是否合法用户,当用户合法后,再通过用户的角色决定能访问的页面。 
具体步骤: 
1、创建一个网站,结构如下: 
网站根目录 
Admin目录 ----> 管理员目录 
Manager.aspx ----> 管理员可以访问的页面 
Users目录 ----> 注册用户目录 
Welcome.aspx ----> 注册用户可以访问的页面 
Error目录 ----> 错误提示目录 
AccessError.htm ----> 访问错误的提示页面 
default.aspx ----> 网站默认页面 
login.aspx ----> 网站登录页面 
web.config ----> 网站配置文件 
2、配置web.config如下: 
复制代码 代码如下:
<configuration> 
<system.web> 
<!--设置Forms身份验证--> 
<authentication mode="Forms"> 
<forms loginUrl="Login.aspx" name="MyWebApp.APSXAUTH" path="/" protection="All" timeout="30"/>
</authentication> 
<authorization> 
<allow users="*"/> 
</authorization> 
</system.web> 
</configuration>

<!--设置Admin目录的访问权限--> 
<location path="Admin"> 
<system.web> 
<authorization> 
<allow roles="Admin"/> 
<deny users="?"/> 
</authorization> 
</system.web> 
</location> 
<!--设置Users目录的访问权限--> 
<location path="Users"> 
<system.web> 
<authorization> 
<allow roles="User"/> 
<deny users="?"/> 
</authorization> 
</system.web> 
</location>

3、在login.aspx页面的登录部分代码如下: 
复制代码 代码如下:
protected void btnLogin_Click(object sender, EventArgs e) 

//Forms身份验证初始化 
FormsAuthentication.Initialize(); 
//验证用户输入并得到登录用户,txtName是用户名称,txtPassword是登录密码 
UserModel um = ValidUser(txtName.Text.Trim(),txtPassword.Text.Trim()); 
if (um != null) 

//创建身份验证票据 
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, 
um.Name, 
DateTime.Now, 
DateTime.Now.AddMinutes(30), 
true, 
um.Roles,//用户所属的角色字符串 
FormsAuthentication.FormsCookiePath); 
//加密身份验证票据 
string hash = FormsAuthentication.Encrypt(ticket); 
//创建要发送到客户端的cookie 
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash); 
if (ticket.IsPersistent) 

cookie.Expires = ticket.Expiration; 

//把准备好的cookie加入到响应流中 
Response.Cookies.Add(cookie);

//转发到请求的页面 
Response.Redirect(FormsAuthentication.GetRedirectUrl(um.Name,false)); 

else 

ClientScriptManager csm = this.Page.ClientScript; 
csm.RegisterStartupScript(this.GetType(), "error_tip", "alert('用户名或密码错误!身份验证失败!');", true);


//验证用户 
private UserModel ValidUser(string name, string password) 

return new UserService().Validate(name, password); 
}

4、给网站添加处理程序Global.asax,其中通用身份验证代码如下: 
复制代码 代码如下:
//改造原来的User,给其添加一个用户所属的角色数据 
protected void Application_AuthenticateRequest(object sender, EventArgs e) 

if (HttpContext.Current.User != null ) 

if (HttpContext.Current.User.Identity.IsAuthenticated) 

if (HttpContext.Current.User.Identity is FormsIdentity) 

FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity; 
FormsAuthenticationTicket ticket = id.Ticket;

string userData = ticket.UserData; 
string[] roles = userData.Split(','); 
//重建HttpContext.Current.User,加入用户拥有的角色数组 
HttpContext.Current.User = new GenericPrincipal(id, roles); 



}

5、在Admin目录中Manager.aspx页面加载代码如下: 
复制代码 代码如下:
protected void Page_Load(object sender, EventArgs e) 

//判断通过身份验证的用户是否有权限访问本页面 
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity; 
//判断通过身份验证的用户是否是Admin角色 
if (!id.Ticket.UserData.Contains("Admin")) 

//跳转到访问权限不够的错误提示页面 
Response.Redirect("~/Error/AccessError.htm", true); 


//安全退出按钮的代码 
protected void btnExit_Click(object sender, EventArgs e) 

//注销票据 
FormsAuthentication.SignOut(); 
ClientScriptManager csm = this.Page.ClientScript; 
csm.RegisterStartupScript(this.GetType(), "exit_tip", "alert('您已经安全退出了!');", true);
}

6、在Users目录中Welcome.aspx页面加载代码如下: 
复制代码 代码如下:
protected void Page_Load(object sender, EventArgs e) 

//判断通过身份验证的用户是否有权限访问本页面 
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity; 
//判断通过身份验证的用户是否是User角色 
if (!id.Ticket.UserData.Contains("User")) 

//跳转到访问权限不够的错误提示页面 
Response.Redirect("~/Error/AccessError.htm", true); 


//安全退出按钮的代码 
protected void btnExit_Click(object sender, EventArgs e) 

//注销票据 
FormsAuthentication.SignOut(); 
ClientScriptManager csm = this.Page.ClientScript; 
csm.RegisterStartupScript(this.GetType(), "exit_tip", "alert('您已经安全退出了!');", true);
}

测试结果: 
数据: 
假设有3个用户,如下: 
------------------------------------------ 
用户名 密码 角色字符串 
------------------------------------------ 
sa sa Admin,User 
admin admin Admin 
user user User 
------------------------------------------ 
测试: 
如果使用admin登录,只能访问Admin目录的Manager.aspx页面; 
如果使用user登录,只能访问Users目录的Welcome.aspx页面; 
使用sa登录,既能访问Admin目录的Manager.aspx页面,又能访问Users目录的Welcome.aspx页面。 
注意:测试时注意及时点击安全退出按钮,否则影响测试结果。 

 

讲解案例4:

1.使用Forms验证存储用户自定义信息

Forms验证在内部的机制为把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,因为是经过特殊加密的,所以应该来说是比较安全的。而.net除了用这个票据存放自己的信息外,还留了一个地给用户自由支配,这就是现在要说的UserData。

UserData可以用来存储string类型的信息,并且也享受Forms验证提供的加密保护,当我们需要这些信息时,也可以通过简单的get方法得到,兼顾了安全性和易用性,用来保存一些必须的敏感信息还是很有用的

下面来看怎么使用UserData,然后会给出一个实际使用的例子。

//创建一个新的票据,将客户ip记入ticket的userdata 
FormsAuthenticationTicket ticket=new FormsAuthenticationTicket( 
1,userName.Text,DateTime.Now,DateTime.Now.AddMinutes(30), 
false,Request.UserHostAddress); 
//将票据加密 
string authTicket=FormsAuthentication.Encrypt(ticket); 
//将加密后的票据保存为cookie 
HttpCookie coo=new HttpCookie(FormsAuthentication.FormsCookieName,authTicket); 
//使用加入了userdata的新cookie 
Response.Cookies.Add(coo); 

下面是FormsAuthenticationTicket构造函数的重载之一的方法签名 
public FormsAuthenticationTicket( 
int version, 
string name, 
DateTime issueDate, 
DateTime expiration, 
bool isPersistent, 
string userData 
); 

参数 
version 
版本号。 
name 
与身份验证票关联的用户名。 
issueDate 
Cookie 的发出时间。 
expiration 
Cookie 的到期日期。 
isPersistent 
如果 Cookie 是持久的,为 true;否则为 false。 
userData 
将存储在 Cookie 中的用户定义数据

使用userdata也很简单,FormsIdentity的Ticket属性就提供了对当前票据的访问,获得票据后就可以用UserData属性访问保存的信息,当然是经过解密的。 
((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData 

下面是一个具体的应用。

由于Forms验证是通过cookie来进行的,它需要传递一个票据来进行工作。虽然票据是加密的,里面的内容不可见,但这并不能阻止别人用一个假冒的身份使用票据(就像我们可以拿别人的钥匙去开别人的锁),比较常见的就是不同ip的用户在不安全通道截获了这个票据,然后使用它进行一些安全范围外的活动。

解决这个问题的办法之一就是使用SSL来传递信息。

但是如果不能使用SSL呢?我们可以判断ip和票据是否匹配,如果发出请求的ip是初次产生票据的ip,则没有问题,否则就销毁这个票据。

为此,我们需要在一开始处理登录时将用户的ip保存起来,这样就可以在以后的请求中随时验证后继请求的ip是否和初始ip相同。保存这个敏感ip的最佳场所当然是UserData啦,而验证的时机则是在AuthenticateRequest事件发生时,即Global.aspx.cs中定义的处理此事件的Application_AuthenticateRequest方法中。

上面的示例实际上已经是把用户ip保存到了UserData中,下面是验证的过程。

if(this.Request.IsAuthenticated) 

if(((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData !=this.Request.UserHostAddress) 

System.Security.Principal.GenericIdentity gi=new System.Security.Principal.GenericIdentity("",""); 
string[] rolesi={}; 
System.Security.Principal.GenericPrincipal gpi=new System.Security.Principal.GenericPrincipal(gi,rolesi); 
this.Context.User=gpi; 

}

通过给GenericPrincipal空的GenericIdentity和roles使票据失效,这样将强迫用户重新登录。为了测试这个方法,可以先把条件改为相等,看效果如何 :)

这个方法也有不足之处,具体为:

1.使用同一代理的用户将拥有同一个ip,这样就不能防范此类假冒攻击了

2.如果用户使用动态ip,则可能造成正常用户被我们强行销毁票据。不过总的来说,这个办法还是比较可行的。

FormsAuthenticationTicket基于forms的验证

构建基于forms的验证机制过程如下: 
 1,设置IIS为可匿名访问和asp.net web.config中设置为form验证 
 2,检索数据存储验证用户,并检索角色(如果不是基于角色可不用) 
 3,使用FormsAuthenticationTicket创建一个Cookie并回发到客户端,并存储 
  角色到票据中,如: 
  FormsAuthentication.SetAuthCookie(Username,true | false) 
  cookies保存时间: 
  HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName].Expires=DateTime.Now.AddDays(1)
  
  如果需要存储角色,采用: 
 FormsAuthenticationTicket authTicket = new 
 FormsAuthenticationTicket( 
  1, // 版本号。 
  txtUserName.Text, // 与身份验证票关联的用户名。 
  DateTime.Now, // Cookie 的发出时间。 
  DateTime.Now.AddMinutes(20),// Cookie 的到期日期。 
  false, // 如果 Cookie 是持久的,为 true;否则为 false。 
  roles ); // 将存储在 Cookie 中的用户定义数据。 
  roles是一个角色字符串数组 

  string encryptedTicket = FormsAuthentication.Encrypt(authTicket); //加密 
  
  存入Cookie 
  HttpCookie authCookie = 
  new HttpCookie(FormsAuthentication.FormsCookieName, 
  encryptedTicket); 
  
  Response.Cookies.Add(authCookie); 
  
 4,在Application_AuthenticateRequest事件中处理程序中(Global.asax)中,使用 
  票创建IPrincipal对象并存在HttpContext.User中 
  代码: 
  HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName]; 
  FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);//解密 
  string[] roles = authTicket.UserData.Split(new char[]{';'});//根据存入时的格式分解,;或|.... 
  Context.User = new GenericPrincipal(Context.User.Identity, Roles);//存到HttpContext.User中 
  
 判断某个角色验证 
 HttpContext.Current.User.IsInRole(roles) //在网页中可以调取,是通过上面最后一行代码添加进入的。

                                                                         //GenericPrincipal对象User中,自带私有m_role

 具体实现 
  
 Web.config文件 
 加入节点,name为COOKIE名称,loginUrl为没有通过验证跳转的地址 
 <system.web> 
  <authentication mode="Forms"> 
  <forms name="Hstear" 
 loginUrl="login.aspx" protection="All" path="/" timeout="40"/> 
  </authentication> 
 </system.web> 
 设置目录访问 path为目录名,roles为票据中的角色名 
 发现网上的都说要单独一个WEB.CONFIG文件放在目录中,但实际在根目录中设置即可,单个文件也一样 
 <location path="Admin"> 
  <system.web> 
  <authorization> 
  <allow roles="admin"/> 
  <deny users="*"/> 
  </authorization> 
  </system.web> 
 </location> 
 Global.asax文件 
 Application_AuthenticateRequest事件中加入 
  protected void Application_AuthenticateRequest(Object sender, EventArgs e)   {  

string cookieName = FormsAuthentication.FormsCookieName;  

HttpCookie authCookie = Context.Request.Cookies[cookieName];  

FormsAuthenticationTicket authTicket = null;  

try  {   

authTicket = FormsAuthentication.Decrypt(authCookie.Value);  

}  catch(Exception ex)  {   return;  }    string[] roles = authTicket.UserData.Split(new char[]{','});//如果存取多个角色,我们把它分解    FormsIdentity id = new FormsIdentity( authTicket );    GenericPrincipal principal = new GenericPrincipal(id, roles);  Context.User =principal;//存到HttpContext.User中     }   
 
原理,将用户角色信息保存在票据中,通过Global.asax,WEB.CONFIG中的设置,判断角色的权限


在Authentication的form元素中,timeout表示:
指定以整数分钟为单位的时间量,超过此时间量,Cookie 将过期。默认值是 30。如果 SlidingExpiration 属性为 true,则 timeout 属性是一个弹性值,以收到最后一个请求后指定的分钟数为到期时间。为避免危及性能,以及为避免向启用 Cookie 警告的用户显示多个浏览器警告,在经过了超过一半的指定时间后更新该 Cookie。这可能导致精确性上的损失。持久性 Cookie 不超时。


0 0