2004-8-14+ 深入探讨Forms验证
1.受Forms验证保护的对象
Forms验证只保护asp.net文件。他保护注册到asp.net的aspx文件、asmx文件及其他文件,但是不保护不属于asp.net的文件-如,文件扩展名为.htm或.html的文件。因为asp.net从来看不到对没有注册到它的文件的请求。
2.web.config的location节
使用location节可以很容易的在根目录下的web.config设置整个应用程序的保护范围。下面是一个示例。
<location path="secret">
<system.web>
<authorization>
<allow roles="coder,manager"></allow>
<deny users="*"/>
</authorization>
</system.web>
</location>
<location path="secret/manage.aspx">
<system.web>
<authorization>
<allow roles="manager"></allow>
<deny users="*"/>
</authorization>
</system.web>
</location>
<location path="coo.aspx">
<system.web>
<authorization>
<allow roles="coder"></allow>
<deny users="*"></deny>
</authorization>
</system.web>
</location>
使用location节来指定了三个不同的保护措施,其中secret/manage.aspx是在secret的保护级别里又加了一层。
3.自由设定验证cookie的超时时间。
在FormsAuthentication.SetAuthCookie(string,bool)方法中,有个参数可以设定验证cookie是临时的还是永久的,如是false,就是临时,时间按web,config中的设定时间来算。如果true,那就是永久的,默认值为50年(……),但是我们可以通过编程的方式来改变这个数字,这就实现了很多地方有“选择cookie保留时间”的功能。
//给用户添加验证凭证
FormsAuthentication.SetAuthCookie(username.Text,true);
//获取验证cookie,FormsAuthentication.FormsCookieName的值就是cookie的name默认是.ASPXAUTH,可以在web.config更改
HttpCookie coo=Response.Cookies[FormsAuthentication.FormsCookieName];
//根据用户对RadioButtonList的选择改变cookie的超时时间
switch(timeout.SelectedValue)
{
case "1" :
coo.Expires=DateTime.Now+new TimeSpan(0,0,1,0);
break;
case "10" :
coo.Expires=DateTime.Now+new TimeSpan(0,0,10,0);
break;
case "60" :
coo.Expires=DateTime.Now+new TimeSpan(0,0,60,0);
break;
default :
coo.Expires=DateTime.Now+new TimeSpan(0,0,1,0);
break;
}
4.获取用户信息
这个问题就是,通过Forms验证后,怎么才能得到用户名。其实也简单,通过Page的User属性来完成这个任务。User属性返回一个IPrincipal对象,然后IPrincipal有个Identity属性,可以获取当前用户的标识。
再说一下这个User,是为当前 HTTP 请求获取或设置安全信息。HttpContext.User 属性提供对 IPrincipal接口的属性和方法的编程访问。
IPrincipal对象,表示代码为其运行的用户的安全上下文,包括用户的标识和任何它们所属的角色。
//IPrincipal接口的属性Identity
Response.Output.Write(Page.User.Identity.Name+"<br>");
Response.Output.Write(Page.User.Identity.AuthenticationType+"<br>");
Response.Output.Write(Page.User.Identity.IsAuthenticated+"<br>");
//IPrincipal接口的方法IsInRole
Response.Write(Page.User.IsInRole("manager"));
5.使用角色。
上面演示location节时用的验证方法都是基于角色的验证。角色这个词可以参照mssql里的角色,就是一系列不同的权限,比如只有角色是manager的人才可以查看secret/manage.aspx。
具体用法就是在定义用户的同时添加一个role字段,这个字段为该拥护分配角色。然后在进行验证的时候通过Global.asax的Application_AuthenticateRequest事件在用户验证时将该用户的角色添加到用户凭证中,最后web.config根据用户的角色判断该用户是否有权力查看相应的页面。
注意我这里说的是把用户资料存在数据库中的情况。下面的代码放在Global.asax.cs文件中。
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
//获得当前HttpApplication,定义 ASP.NET 应用程序中的所有应用程序对象通用的方法、属性和事件。
HttpApplication app=(HttpApplication)sender;
//检查用户是否通过了验证及验证类型是否为forms验证
if(app.Request.IsAuthenticated && app.User.Identity is FormsIdentity)
{
//将当前用户的身份转成formsidentity
FormsIdentity fid=(FormsIdentity)app.User.Identity;
//获取该用户的角色
string role=getRole(fid.Name);
//通过设置GenericPrincipal,来将roles添加到当前用户。GenericPrincipal:此类表示当前用户的角色。
if(role!=null)
app.Context.User=new GenericPrincipal(fid,new string[] {role});
}
}
//获取用户的角色
private string getRole(string name)
{
string strconn=ConfigurationSettings.AppSettings[0];
SqlConnection conn=new SqlConnection(strconn);
string sql="select role from users where username= @username";
SqlCommand cmd=new SqlCommand(sql,conn);
SqlParameter uname=new SqlParameter("@username",SqlDbType.VarChar,50);
uname.Value=name;
cmd.Parameters.Add(uname);
try
{
conn.Open();
object role=cmd.ExecuteScalar();
if(role is DBNull)
return null;
return (string)role;
}
catch(SqlException)
{
return null;
}
finally
{
conn.Close();
}
}
比如说username:Notus/password:wei/role:coder。该用户在登陆时对其进行Forms验证,如通过,则给其设定验证凭据,同时Application_AuthenticateRequest事件启动,在该凭据内加入用户的角色信息,在这里为coder。这样用户可以访问coo.aspx,因其角色为coder,也可以访问secret中的文件,但是不可以访问secret/manage.aspx,因为按web.config的设定,只有角色为manager的才可以访问该文件。
6.用户合法性验证
下面是我常用的代码,顺便贴出来。
public bool check(string uid,string pwd)
{
string strconn=ConfigurationSettings.AppSettings[0];
SqlConnection conn=new SqlConnection(strconn);
string sql="select password from users where username=@username";
SqlCommand cmd=new SqlCommand(sql,conn);
SqlParameter uname=new SqlParameter("@username",SqlDbType.VarChar,50);
uname.Value=uid;
cmd.Parameters.Add(uname);
try
{
conn.Open();
object password=cmd.ExecuteScalar();
if(password is DBNull)
return false;
if((string)password!=pwd)
return false;
return true;
}
catch(SqlException ex)
{
return false;
}
finally
{
conn.Close();
}
}