[转]《asp.net web站点高级编程》用户和验证分析
转自:CSDN TomMax(笑望人生)
大家注意,研究这个功能要使用三个类,而不是Principal和Identity两个类。
我先说一下Principal和Identity两个类的实现原理。
Asp.net内置的Forms验证功能非常强大,不过它仅仅支持存放在web.config文件中以xml格式存放的登录内容。他可以以HttpContext类的一个User属性的方式来指示用户的身份信息。
如果我们启用了Forms的身份验证,而且我们在web.config中配置了用户信息的话,我们就可以在任何.aspx的后台代码中这样写(假设使用page_load):
private void Page_Load(object sender ,EventArgs e)
{
if(Page.User.IsAuthenticated)
{
//用户登录后代码
}
else
{
//用户未登录代码
}
}
以上的Page.User.IsAuthenticated这句代码是关键,我们并没有执行任何代码,没有指定任何类,那么asp.net的运行时是如何确定用户登录的?我们必须研究一下asp.net使用forms方式确定用户登录的流程。
从Page.User.IsAuthenticated这个代码我们可以知道,调用了System.Web.UI.Page类中一个名叫User属性返回的类中的一个名叫IsAuthenticated的值,这个值是bool类型,确定用户是否登录。好,那么我们来看一下.net的类库参考,看看这个User属性返回的是什么东西。
打开vs.net2003的类库帮助,找到System.Web.UI.Page成员中的User属性(地址:ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfSystemWebUIPageClassUserTopic.htm),看到就明白了,这个属性实际上是一个指针,返回了一个名叫IPrincipal的类。好,我们在继续看,看看这个IPrincipal类是个什么东西。
打开这个类的帮助(地址是:ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfsystemsecurityprincipaliprincipalclasstopic.htm),明白了,这个不是一个类,而是一个接口,专门用来定义一个负责安全环境的,专门用来处理所有关于登录,用户,安全访问的内容。(注意:与Page.User属性返回的类不同),.net框架已经有两个类实现了这个接口,这两个类一看就明白,分别是GenericPrincipal和WindowsPrincipal两个类。
看了这两个类大家肯定明白:我们Web上面用的肯定是GenericPrincipal,表示一般的安全环境。
看到这里,大家可能不明白,为什么要用一个IPrincipal接口来定义安全环境呢?直接定义不同的用户对象不就完了吗,干嘛还要这么麻烦?因为asp.net支持不同的安全环境(其实就是asp.net的验证模式,比如现在有的Forms表单认证,Windiws域用户身份验证,还有时髦和要花钱的passport验证。为了实现这些不同的验证,肯定要定义一个通用的标准,要让符合这个标准的所有的类都可以被asp.net的运行时容易的识别。还有,你知道在asp.net1.5版,2.0版本等等以后的版本里面不加入更多的身份验证模式,如果有了这个接口,那么新的验证模式只要继承这个接口,很容易就可以被asp.net使用,这样多方便?
到现在为止,上面说的IsAuthenticated属性还是没有头绪,我们来继续,看看GenericPrincipal类返回的到底是个什么东西,打开类库参考(地址:ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfsystemsecurityprincipalgenericprincipalclasstopic.htm),原来这个也是一个接口,终于发现了这个接口返回的是用户的信息了,实现这个接口的四个类中,FormsIdentity类肯定就是我们需要的东西。看看FormsIdentity的帮助(地址:ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfsystemwebsecurityformsidentityclasstopic.htm)在成员页中,终于看见了IsAuthenticated的属性,原来绕了一大圈我们使用Page.User.IsAuthenticated就是在这里调用的。
根据FormsIdentity概述里面说的,这个类是提供要由 FormsAuthenticationModule 使用的类。而根据asp.net的forms验证,肯定是与amazon和yahoo网站同样的是使用Cookie来验证用户身份,那么FormsAuthenticationModule类肯定就是处理Cookie的保存和处理的类了。
说到这里,我们很容易可以推出来用户登录验证代码if(Page.User.IsAuthenticated)asp.net运行时执行的顺序:
首先System.Web.UI.Page的实例开始运行,在内部初始化的代码中(仅是验证用户流程):
首先调用HttpContext类实例-->调用这个类的User属性-->调用实现IPrincipal接口的GenericPrincipal类实例-->调用这个类里面的Identity成员-->调用符合IIdentity接口的FormsIdentity类实例-->由这个类的构造函数实现一个FormsAuthenticationModule类的类实例,从客户端提交的http头信息中读取Cookie内容,再通过另外代码读取保存在web.config中的内容来判断用户是否登录和获取用户身份信息。从而结束这个代码的使用。
看起来是不是非常复杂?我们如果要扩展asp.net的登录验证功能,必须同样实现上面的步骤,所有的类都必须自己重写。好,下面我来说怎样实现扩展asp.net的验证,作个DataBaseIdentity类(我自己起的名字),实现支持数据库验证
?
根据上面的处理流程,我们要实现使用Page.User.XXXX这样的代码来实现我们自己的数据库验证,必须也从头把上面的代码重新来一遍,下面是过程(仅提供思路):
第一步:
处理客户端的Cookie,asp.net已经为我们提供了FormsAuthenticationModule类来实现这个功能,它保存的Cookie还可以加密,不过他提供的保存Cookie的方法不是太多。我们可以自己写一个。这个类我们可以实现如下功能(假设名称为CookieProcess):
构造函数:获取保存在用户Cookie集合内的固定内容,保存在变量里面,通过属性输出,如用户是否登录,用户ID,用户名等。如果用户没有Cookie,设置一个flag值表示没有Cookie。
用户登录后保存Cookie:就是保存用户的登录Flag值,用户名和ID,调用加密(可以使用DES加密,源代码在C#FAQ里面)方法加密内容
用户注销后删除Cookie :使用一个for循环,把用户所有的Cookie全部删除
保存用户自定义的Cookie值:当然这个处理Cookie的类不能仅仅实现验证Cookie,还有许多信息可以保存在Cookie,这个就是了。
读取用户自定义Cookie值:与上面方法相反,读取内容供使用
加密和解密方法:方法可以是私有的,用来加密和解密。
第二步:不是实现IIdentity接口,而是要做一个使用数据库来验证用户的几个类,比如说用户信息类:负责用户名与密码的验证,用户注册,获取用户信息;角色类:给用户分组,权限类,负责定义权限等。《asp.net web站点高级编程》已经说的很清楚,不用我多说。
第三步:该实现IIdentity类了,我们可以不用《asp.net web站点高级编程》里面的类名称,可以自己搞一个什么DataBaseIdentity类,当然要继承System.Security.Principal.IIdentity的接口,定义两个构造函数,就是一个有参数,一个没有参数,来检测用户是否已经登录,如果通过Cookie得知已经登录,就调用第二个构造函数,通过ID来获取用户的详细信息(同过调用上面写的数据库用户操作类)放到属性里面输出,如用户昵称,用户名,判断用户是否登录,用户角色列表和权限列表等。
第二个构造函数负责根据用户ID读取,就是已经登陆的用户获取ID后直接使用数据库操作类来返回信息。
第四步:建立符合IPrincipal接口的类,这个类是专用于HttpContext.User返回的类,处理各种方法就写在这个里面,名称是DataBasePrincipal.
构造函数写一个,参数使用用户ID,里面的方法除了IPrincipal接口成员外,还要加上,登录后注销的方法,验证角色和身份的方法等。因为我们没有使用FormsAuthentication类来帮助我们调用,我们就自己写SignOut()方法来调用CookieProcess类里面的删除Cookie方法进行注销操作。其他的验证方法就可以调用数据库操作类来完成。
到了这时候,实际上登录及权限处理系统已经完成,但是不能使用。这就是好多人看《asp.net web站点高级编程》不明白的原因。很简单,我们的所有aspx的后台代码默认继承的都是System.Web.UI.Page类,这个类的Load事件根本就不会执行我们上面定义的这些代码,所以,无论我们如何做,都无法达到这个效果。所以解决办法只有一个,那就是:
第五步:建立自己的aspx页面基类
在《asp.net web站点高级编程》中如果大家注意看,有个PhilePage类,在书上P157页,通过加入事件处理代码来实现自己的OnLoad事件处理程序,我们在这里做文章,把我们建立的类给集成到asp.net的运行时里面去。这个类我起名叫BasePage
代码如下:
//首先引入必要的名称空间,这里略。
public abstract class BasePage:System.Web.UI.Page
{
public BasePage(){ /* 构造函数无操作 */ }
// 重写OnInit处理程序,必须加入自己的Onload
protected override void OnInit(EventArgs e)
{
//这是Page类的处理程序,必须先调用
base.OnInit(e);
//然后加入我们自己的
this.Load += new System.EventHandler(this.BasePage_Load);
}
// 这就是自己的Load处理了,这里加入用户处理的类
private void BasePage_Load(object sender,EventArgs e)
{
//注意,这里我们可以加入好多有用的代码,
//所有的aspx都需要用的功能都可以写在这里
//下面调用DataBaseIdentity类实例
//即调用我们自己写的验证方法,而不是
//使用asp.net提供的FormsAuthentication方法
//所以if (Context.User.Identity.IsAuthenticated)
//给注释掉,可以使用我们自己的验证用户登录的办法
//我假设使用了CookieProcess类自己的验证方法
if(CookieProcess.IsLoged)
{
//使用上面建立的DataBasePrincipal的构造函数,新建
//DataBasePrincipal实例,通过Cookie获得用户ID
DataBasePrincipal newuser = new DataBasePrincipal(CookieProcess.UserID);
//然后把这个实例给HttpContext.User属性即可
HttpContext.User = newuser;
}
}
}
然后,我们把所有的aspx后台代码全部继承这个BasePage,就能实现用户管理了。
注意,我的这个实现与《asp.net web站点高级编程》的实现有些地方不同。《asp.net web站点高级编程》的实现使用了asp.net提供的FormsAuthentication来实现用户的注册,登录注销等功能。而我是全部自己实现的,所以有些地方与书上写的不同。
另:这个思路有个不同的地方,就是那个写在DataBaseIdentity类里面的无参数构造函数里的验证代码根本不需要,验证功能写在basepage类里面就行了。
浙公网安备 33010602011771号