asp.net 2.0窗体身份验证机制详解(formsauthentication)
本篇文章介绍了在asp.net 2.0如何做窗体身份验证,并且讲解了iis和asp.net2.0窗体身份验证机制是如何结合在一起的。我们还会详细讲解一下2.0中关于窗体身份验证的一个类:formsauthenticationmodule。
l 综述
当某一个用户使用用户名成功登陆网站时,formsauthentication(窗体身份验证机制,下面统一使用英文术语) 将会创建一个authentication ticket (身份验证票),通过这个ticket就可以在网站上全程跟踪这个用户了。form authentication ticket通常被包含在一个cookie里面,但是asp.net2.0也支持不使用cookie的formsauthentication,这时候ticket就需要通过query string 传递。
当一个用户在登陆某个网站时,需要提供身份验证才能进入网站。如果他还没有输入验证信息(通常是用户名和密码),则此用户将会被重定向到一个登陆页面。用户可以在登陆页面输入验证信息,然后这些信息被发送到服务器与某一个存储用户身份信息的介质(例如sql server或者某个文件)进行信息对比。在asp.net2.0中,可以通过membership proivder来访问存储在诸如sqlserver的信息。(provider模式有很多优点,稍后会有文章详细说明。)当用户信息通过验证后,此用户将获得允许,访问他所期望的页面。
formsauthentication通过formsauthenticationmodule这个类来执行,这个类是asp.net页面运行周期的一部分。以下我们将解释formsauthentication在asp.net2.0中是如何工作的。
l iis 验证
asp.net验证分为两步。首先,iis验证当前用户访问网站所使用的windows帐号是否有权限。如果iis访问被配置为anonymous,则任何用户都能访问页面。
然后,在iis验证完毕后,asp.net开始执行自身的验证。验证模式可以在web.config文件中配置,只要在config文件中写上<authentication mode="forms" />,那么asp.net就知道使用formsauthenticationmodule 类进行验证。
l asp.net froms authentication
froms authentication 配置:你可以在config文件中配置。配置如下:
<system.web>
<authentication mode="forms">
<forms loginurl="login.aspx"
protection="all"
timeout="30"
name=".aspxauth"
path="/"
requiressl="false"
slidingexpiration="true"
defaulturl="default.aspx"
cookieless="usedeviceprofile" enablecrossappredirects="false" />
</authentication>
</system.web>
这些属性详细描述如下:
loginurl:指向登陆页面,你应改把登陆页面放在一个需要secure sockets layer (ssl)的文件夹里。这样才能保障帐号的安全和完整。
potection:设置为”all”表明authentication ticket是加密的,加密算法被定义在machinekey这个元素中,并且通过哈希算法进行签名,这个算法也定义在machinekey中。
timout:该属性定义了验证session的过期时间。默认值为30分钟。
requiressl:该属性被设置为false,这表明验证的cookies可以不同过ssl加密传输。如果对session的安全性特别重视的话,则需要设置为true。
slidingexpiration:该属性被设置为true,这表示用户只要在网站上持续保持活动,则session就不会过期。
defaulturl:该属性表示登陆后的默认页面。
cookieless:该属性被设置为usedeviceprofile,这表示cookie将在任何支持它的浏览器中使用,如果该浏览器不支持cookie的话,form authentication将通过url来传递authentication ticket
enablecrossappredirects:该属性表示是否可以将通过身份验证的用户重定向到其他web应用程序。true表示可以,false表示不可以。
l authorization configuration(用户配置)
类urlauthorizationmodule用来执行保障只有通过身份验证的用户才能访问页面。你可以在web.config文件中配置这个类:配置如下:
<ystem.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
以上配置表明,没有通过验证的用户都将被拒绝访问页面。如果一个没有验证的用户试图访问页面,他将会被重定向到loginurl属性定义的登陆页面上。
l forms authentication 控制流程
forms authentication的流程可以参考下图:
我们来分析一下上面的流程:
第一步:用户访问default.aspx页面,iis通过了验证,asp.net发现 authorization
元素中包含<deny users="?" />的标签。
第二步:服务器寻找包含验证信息的cookie,如果没有找到这个cookie,用户将被重定向到登陆页面。就是loginurl所指定的页面。用户将在那个页面输入登陆信息。:
第三步:浏览器请求浏览登录页面,同时传递returnurl的参数的值。
第四步:服务器调转到登陆页面。
第五步:用户输入身份验证信息,并且提交数据,其中还包含returnurl的参数值。
第六部:服务器通过读取存储介质(例如sqserver数据库)验证用户的信息。登陆页面将创建一个包含form authentication ticket的cookie作为session。
在asp.net2.0身份验证可以通过membership系统。membership类提供了validateuser的方法,参考如下:
if (membership.validateuser(username.text, password.text))
{if (request.querystring["returnurl"] != null) {formsauthentication.redirectfromloginpage(username.text,false); }
else { formsauthentication.setauthcookie(username.text, false); } }
else { response.write("invalid userid and password");
}
第七步:用户验证成功,服务器重新让浏览器指向retururl所指定的页面。
第八步:在重定向的同时,浏览器向default.aspx页面发送request请求,此次请求包含用户的forms authentication cookie。
第九步:formsauthenticationmodule类侦测到forms authentication cookie并且开始验证,验证成功后,该类将得到当前的用户信息,并传送给httpcontext对象。可以通过httpcontext对象获得当前用户的信息。
第10步:验证成功,来去自如哦!
l formsauthenticationmodule
asp.net 2.0在系统默认的web.config文件中定义了一系列的http模块(http modules).其中包括了一系列的验证模块如下:
<httpmodules>
...
<add name="windowsauthentication" type="system.web.security.windowsauthenticationmodule" />
<add name="formsauthentication" type="system.web.security.formsauthenticationmodule" />
<add name="passportauthentication" type="system.web.security.passportauthenticationmodule" /> ... </httpmodules>
在每一次请求时,只能使用一种验证模块。通常验证模式会被定义在web.config文件中:
<authentication mode="forms" />这句话表示使用formsauthentication
formsauthenticationmodule类会创建一个genericprincipal的对象,然后把它存入http context中。genericprincipal包含一个formsidentity的实例的引用,formsidentity实例包含了用户的信息。一般你会通过forms authentication来替你完成以上工作。但是如果你的程序还有别的特殊要求,比如把用户信息传递给一个自定义的类(该类继承iprincipal接口),你的程序需要在postauthenticate event事件中编写代码。postauthenticate event事件会在 formsauthenticationmodule验证forms authentication cookie 并且创建完genericprincipal和formsidentity对象后触发,在该事件触发后,你可以在事件中创建自定义的iprincipal对象,用此对象封装formsidentity对象,然后把自定义的iprincipal对象存入httpcontext中。
注意:如果自定义ipricipal对象,你必须在当前线程中设置自定义对象的引用:例如:thread.currentprincipal=newgenericprincipal(new genericidentity( "bob", "passport"), rolesarray);
这样才能保证httpcontext对象和线程指向同一个验证用户的信息。
l forms authentication cookies
formsauthentication类在调用formsauthentication.setauthcookie或者formsauthentication.redirectfromloginpage的方法后会自动创建验证cookie;
一个典型的验证cookie包含以下两个属性:
name:cookie的名称
value:cookie的值
在一个典型的forms authentication cookie中,cookie的值是加密的,并且创建一个formauthenticationticket的签名。cookie包含以下属性:
expires:此属性标识cookie的过期时间,当用户需要把cookie保存在本地电脑上时,需要设置此属性。
domain:这个属性表明cookie和哪个域相关联,默认的值为null
haskeys:这个属性表明cookie是否有子键。
httponly: 这个属性表示cookie是否能被客户端脚本读取,.net2.0中,这个设置始终为true;但在客户端浏览器中,只有ie6.0才能识别这个属性。
path:这个属性表明cookie的虚拟目录。默认的值为”/”,表示站点根目录。
secure:这个属性表明cookie是否需要加密。如果设置true,cookie将接受ssl加密
version:这个属性表明cookie的版本号
l 创建forms authentication cookies
当某个用户验证通过后,forms authentication cookies会被forms authentication类在内部自动创建。创建的就是一个formsauthenticationticket类。创建此类的代码如下:
formsauthenticationticket ticket = new formsauthenticationticket(1,
"username",
datetime.now,
datetime.now.addminutes(30), // value of time out property false, // value of ispersistent property
string.empty,
formsauthentication.formscookiepath);
然后如果在web.config文件中,forms元素的protection属性被设置成all
或者encryption,将对ticket对象进行加密并且创建签名。加密代码如下:
string encryptedticket = formsauthentication.encrypt(ticket);
下面将简要介绍一下当protection属性被设置为true时的流程:
n 创建一个序列化的forms authentication ticket:即创建此对象为一个字节数组(byte array)
n 创建forms authentication ticket的签名。machinekey中的validation和alidationkey的属性所设置了生成签名的算法。我们用此算法计算上面序列化的bytearray,生成mac(message authentication code)。在默认的选择中,系统使用的是sha1的算法。
n 加密forms authentication ticket,同时我们将创建另外一个序列化的对象,此对象经过加密算法加密。这个加密算法也可在machinekey中的decryption和decryptionkey的属性中获得。在asp.net 1.1中使用的是3des加密,而在asp.net2.0中,使用的是aes加密算法。
n 创建httpcookie对象或者生成cookie的query string,(在不支持cookie时,我们只能生成query string).httpcookie 对象创建方法代码如下:
httpcookie authcookie = new httpcookie(
formsauthentication.formscookiename,
encryptedticket);
加密后的ticket就被添加到了httpcookie对象中。
n 设置forms authentication cookie为安全的。如果forms authentication ticket被配置成为使用ssl,那么httpcookie. secure的属性也必须设置成true.在这种情况下,浏览器只能通过https协议传送cookies.
n 设置httponly bit,在asp.net2.0中,这个属性默认设置完成。
n 设置当前cookie的属性。如果需要,可以设置当前cookie的path, domain and和expires属性
n 添加cookie至cookiecollection,传给客户端。response.cookies.add(authcookie);
每当authentication收到一个请求,formsauthenticationmodule从客户端cookie获得一个authentication ticket,然后解码,计算哈希值比对mac值,这样就确认了cookie没有被人为伪造。最后验证ticket包含的过期时间。
注 asp.net 并不依赖于 cookie 的到期日期,因为该时间很容易伪造。
l 角色授权(role authorization)
在 asp.net 2.0 中,角色授权已经得到简化。对用户进行身份验证或者将角色细节添加到身份验证 cookie 时,不再需要检索角色信息。.net framework 2.0 包括一个角色管理 api,它使您能够创建和删除角色,将用户添加到角色以及从角色删除用户。该角色管理 api 将其数据存储在一个基础数据存储中,它通过针对该数据存储的适当角色提供程序访问该存储。以下角色提供程序为 .net framework 2.0 附带,可以与窗体身份验证一起使用:
? sql server。它是默认的提供程序,将角色信息存储在 sql server 数据库。
? 授权管理器 (azman)。该提供程序使用 xml 文件、active directory 或 active directory 应用程序模式 (adam) 中的一个 azman 策略存储作为其角色存储。它通常用于 intranet 或 extranet 方案中,其中 windows 身份验证和 active directory 用于进行身份验证。
how to: use role manager in asp.net 2.0.
l cookieless窗体验证
asp.net 2.0 支持 cookieless 窗体身份验证。该功能由 forms 元素的 cookieless 属性控制。该属性可以设置为以下四个值之一:
? usecookies。该值强制 formsauthenticationmodule 类使用 cookie 传输身份验证票。
? useuri。该值指示 formsauthenticationmodule 类重写 url 来传输身份验证票。
? usedeviceprofile。该值指示 formsauthenticationmodule 类查看浏览器功能。如果浏览器支持 cookie,则使用 cookie;否则,重写 url。
? autodetect。该值通过一个动态检测机制指示 formsauthenticationmodule 类检测浏览器是否支持 cookie。如果检测逻辑表明不支持 cookie,则重写 url。
如果应用程序配置为使用 cookieless 窗体身份验证,并且正在使用 formsauthentication.redirectfromloginpage 方法,则 formsauthenticationmodule 类自动设置 url 中的窗体身份验证票。以下代码示例显示了典型 url 在重写后的外观:
http://localhost/cookielessformsauthtest/(f(-k9dcsriy4caw81rbju8krnj5o_goqe0i1e_jnjlym74izyojk8gwdfoebgepjtews0pci7fhgtouftje9jvga2))/test.aspx
括号中的 url 部分包含 cookie 通常将包含的数据。该数据在请求处理过程中由 asp.net 删除。该步骤由 asp.net isapi 筛选器执行,而不是在 httpmodule 类中执行。如果从一个 .aspx 页读取 request.path 属性,您在 url 中不会看到任何额外的信息。如果重定向请求,url 将自动重写。
注:难以保证 url 中包含的身份验证票的安全。当安全性极为重要时,您应该使用 cookie 存储身份验证票。
l membership和logincontrol(成员身份和登录控件)
asp.net 2.0 引入了membership功能和一组登录 web 服务器控件,它们简化了使用窗体身份验证的应用程序的实现。
membership为应用程序用户提供凭据存储和管理。它还提供一个membership api,可以在使用窗体身份验证时简化用户凭据的验证任务。该membership功能构建于提供程序模型之上。该模型允许实现和配置指向不同用户存储的不同提供程序。asp.net 2.0 包括以下成员关系提供程序:
? active directory membership provider。该提供程序使用 active directory 或 active directory 应用程序模式 (adam) 用户存储。
? sql server membership provider。该提供程序使用 sql server 用户存储。
还可以添加对自定义用户存储的支持。例如,可以添加对其他轻量级目录访问协议 (ldap) 目录或其他现有公共标识存储的支持。为此,创建一个从 membershipprovider 抽象基类继承的自定义提供程序。
asp.net 登录控件自动使用membership和窗体身份验证,并封装提示用户输入凭据,验证用户,恢复或替换密码等所需的逻辑。实际上,asp.net 登录控件在窗体身份验证和membership上提供一个抽象层,并且取代了您使用窗体身份验证时通常必须进行的大多数或全部工作。
有关使用membership功能和登录控件的详细信息,请参阅
how to: use membership in asp.net 2.0.
l web farm scenarios(web 场方案)
在 web 场中,无法确保哪个服务器将处理连续请求。如果用户在一台服务器上经过身份验证,但下一个请求在另一台服务器上进行,则身份验证票将导致验证失败并请求用户重新进行身份验证。
machinekey 元素中的 validationkey 和 decryptionkey 属性用于对窗体身份验证票进行哈希操作和加密。这些属性的默认值为 autogenerate.isolateapps。这些密钥是针对每个应用程序自动生成的,在每台服务器上都不同。因此,在一台计算机上加密的身份验证票无法在 web 场中的另一台计算机或者同一台 web 服务器上的另一个应用程序中进行解密和验证。
为了解决该问题, web 场中所有计算机上的 validationkey 和 decryptionkey 值都必须相同。
有关配置 machinekey 元素的详细信息,请参阅 how to: configure machinekey in asp.net 2.0