ASP.NET安全问题--Forms验证的具体介绍(上篇)
本篇的话题如下:
Forms验证的工作原理
Forms验证中的API
Forms验证的工作原理
我们知道,Forms验证主要是基于cookie的,说白一点就是:把用户信息保存在cookie中,然后发送到客户端;再就是解析客户端的发送了的cookie信息,进行解析,然后进行验证。关于cookieless的工作原理和方法,我这里不赘述,大家可以参看我的另外的一片文章:浅谈ASP.NET内部机制(一)。
当匿名用户请求一个需要验证后才能访问的资源和页面的时候,那么如果采用了Forms验证,那么URL授权模块就会把用户重定向到登录页面。而之前请求的URL就会被保存起来,等到用户正确的登录后,就再次转向之前要请求的页面。我想这点,大家应该都用过。
下面我们就看看登录的时候发生了什么,看看登录的具体的流程?也请大家注意我使用的一些术语,因为这些术语再Forms中都有特定的对象,大家之后就可以看到的,很重要。
1.再浏览器中有个登录窗体,要输入用户名和密码等凭证,通过提交给服务器的ASP.NET网站来审核,检查凭证是否正确。
2.如果凭证正确,那么就会再服务器端就会创建一个"身份验证票据"。身份验证票据中含有了经过加密的用户信息。
3.这个票据再服务器端被写入cookie中,然后发送到客户端。
4.然后用户就被重定向到他们最初请求的URL中。
注:大家可能会有疑问:最初请求的URL到底保存在哪里?不要担心,现在只要明白上面的流程就OK。
5.上面第4步就是要转向最初请求的URL,假设最初的请求页面是Default.aspx,那么现在就是从登录的页面Login.aspx转向到Default.aspx 页面,此时因为身份验证的票据cookie已经存在于客户端的浏览器中了,此时的转向Default.aspx页面时,实际是再次向服务器端发起了请求,所以正如我们之前所谈到的:每个请求都要从ASP.NET管道中一级级的向后传,要经历ASP.NET的的生命周期:Application_BeginRequest,Application_AuthenticateRequest.....。(希望大家明白)
但是这次的请求就和第一次我们发起的请求步同了,为什么?
第一次我们请求Default.aspx页面的时候,我们根本就没有提供任何的表明我们身份的票据,但是这次我们已经登录了,而且我们的浏览器中已经有了我们的身份验证的票据的cookie,此时在Application_AuthenticateRequest事件中,Forms验证模块就获取表明我们身份cookie,然后就利用cookie中信息填充Context.User。
验证模块处理完之后就是授权模块起作用了。其实URL授权模块就会利用我们之前填充在Context.User中的信息来验证用户是否被批准访问所请求的资源或者页面。
Forms验证中的API
实现Forms身份验证之前,我们看看组成Forms验证的API以及相关的类:
FormsAuthenticationModule:对每个请求进行验证的HTTP模块
FormsAuthentication:包含在Forms验证中我们常用的方法和属性(很重要的)
FormsIdentity:Forms验证标识。
FormsAuthenticationTicket:身份验证的票据,对用户的信息进行加密后的产物,我们一般把它写如cookie中,之前我们谈过了的。
上面的类在System.Web.Security下。
下面我们来一一介绍.
FormsAuthenticationModule
它是一个实现了IHttpModule接口的类。它可以用来处理每个请求的Application_AuthenticateRequest事件。如果发送了的请求中已经包含了cookie信息,那么这个模块就对cookie信息进行解密和解析,然后构造一个GenericPrincipal的类实例填充Context.User,并且也创建一个FormsIdentity的实例。
注意:当我们在web.config中配置了Forms验证后,那么我们在Application_AuthenticateRequest事件写的代码要是和Forms相关的API。上篇文章谈过了。
FormsAuthentication类 这个类很重要。
还有一点注意的就是:因为FormsAuthentication和FormsAuthenticationModule名称很相似,很容易混淆。
它们之前的区别在于,FormsAuthenticationModule是一个HTTP模块;而FormsAuthenticate是一个类,它有很多的方法和属性。更加直白的说就是:它们之前没有什么关联,只是在Application_AuthenticateRequest事件中我们常常要调用FormsAuthenticate类的一些方法和属性。而且FormsAuthenticate的很多方法都是静态的方法,我们不会创建FormsAuthenticate类的实例。
还有一点要特别注意的就是FormsAuthenticate的Authenticate方法。
我们之前说过了,我们一般是在登录窗体中提交用户信息,然后服务器端验证提交的信息,我们在服务器端常常是去数据库中检查这些信息的正确性,但是去数据库或者其他的数据存储(如文件,活动目录)中去检查只是一种情况。
还有另外的情况。不知道大家是否记得web.config 中的一个配置的节点:
如果我们在配置文件配置了上述的信息,那么我们就可以用Authenticate方法来检查提供了用户信息(用户名和密码)是否正确,如果我们没有在web.config配置用户的信息,也就是说我们是把信息保存在数据库等其他的地方,那么我们就不能Authenticate这个方法。当然我们很少用Authenticate这个方法,因为我们不可能把所有用户信息硬编码到配置文件中,但是还是要清楚这个方法。
另外我简单的介绍一些常用的方法,具体的使用我以后会讲述。
在FormsAuthenticate中使用频繁的是RedirectFromLoginPage方法。每当验证了用户的凭证后就会使用到这个方法,也就是我们之前说过的:跳转到我们最初请求的页面。
这个方法就这么简单的一"跳",但是其实在内部做了很多的事情:
1.为用户创建一个身份验证的票据
2.对身份验证的票据进行加密
3.创建一个cookie,把加密的票据保存在cookie中
4.向HTTP响应添加cookie,并且发送给客户端。
5.跳转,并且把用户重定向到最初请求的页面
另外FormsAuthenticate类还有很多的其他方法和属性:
FormsAuthenticate中涉及到客户端保存cookie的两个属性就是:
FormsCookieName:获取或者设设置cookie的名称
FormsCookiePath:获取或者设置cookie的url路径
其中FormsCookiePath属性有一点要注意:大多数的浏览器会在判断cookie是否要和请求一起发送时,用到cookie路径。(我们一般在配置文件配置path="/"),如果我们配置的path不是"/",那么这个cookie就不会和请求发送到服务器端.
FormsAuthenticate中和cookie操作相关的方法有:
Decrypt:提取身份验证cookie的加密信息,创建FormsAuthenticationTicket,也就是解密。
Encrypt:加密。从FormsAuthenticationTicket中获取信息,并且加密。以备我们之后把加密的信息写入cookie
GetAuthCookie:创建身份验证cookie,但是并不把它立即添加到HTTP响应中
SetAuthCookie:创建身份验证cookie,并且把它添加到Response.Cookie中。
RenewTicketIfOld:刷新身份验证cookie的生命周期
GetRedirectUrl:把用户重定向到他们最初请求的页面。
SignOut:使得当前的身份验证cookie过期。我们常用的注销功能。
FormsIdentity
大家现在应该知道什么是标识 Identity,它包含了用户名和ID标识信息,可以参看我前面的文章。
FormsAuthenticationTicket 票据
通过上面的讲解,大家已经对它不陌生了,FormsAuthenticationTicket实际上就包含用户信息的一个类的实例。
注意:FormsAuthenticationTicket和cookie之间的区别:
cookie其实就是一个载体,容器,它包含了加密后的FormsAuthenticationTicket。
FormsAuthenticationTicket类的UserName属性就是用户的用户名,我们可以根据这个属性识别不同的用户。
由于身份验证是基于cookie的,所以要考虑到cookie的过期的问题。比如我们在登录时有个"记住我"的checkbox,如果勾上,那么就创建了一个永不过期的cookie,处于安全,我不提倡这样。
所以在FormsAuthenticationTicket也提供了关于设置cookie属性:
Expiration:获取一个表示cookie期满的DateTime对象
Expired:判断cookie是否过期
IsPersistent:是否在用户关闭浏览器后继续保存cookie
IssueDate:返回最初设置cookie的时间
还有就是CookiePath:设置cookie的保存路径,前面谈论过了,一般设置为"/"。
另外FormsAuthenticationTicket身份验证票据目的是识别用户。同时,我们也可以利用FormsAuthenticationTicket的UserData属性添加额外的信息,如角色等,然后这额外的信息就可以保存在cookie中。
今天就谈这里。大家先有个总体的认识,具体的代码部分,我们后面谈。谢谢各位!!!