最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
asp.net Session会话层使用与管理方法
时间:2022-06-25 04:15:57 编辑:袖梨 来源:一聚教程网
1. 保留默认的会话模块,但编写自定义的状态提供程序来更改存储介质。这样,我们还有机会重写一些用于在存储与session之间运载会话数据的辅助类。
2. 保留默认的会话模块,但替换会话id生成器。
3. 将默认的会话状态模块替换成自己的。这种方法提供了最好的灵活性,但也最为复杂,建议仅当的确必要且知道确切的实现方法才使用这种方案。
构建自定的会话状态提供程序
会话状态提供程序是一种组件,负责为当前会话数据进行服务。当请求需要状态信息时,该组件便会被调用,从给定的存储介质中获取数据,并返回给主调模块。在请求结束时,也会被调用,以便将提供的数据写入存储层。
asp教程.net支持三种状态提供程序,下表对其作了说明:
我们还可以编写自己的状态提供程序类,使其应用自选的存储介质。
定义会话状态存储
状态提供程序是一种继承于sessionstatestoreproviderbase的类,下表列出了其接口的主要方法:
继承sessionstatestoreproviderbase类,并保留默认的asp.net教程会话状态模块,这种方案只能更改会话状态数据存储和数据恢复这部分功能,其他功能不能更改。
锁定与过期
状态提供程序必须实现会话连续访问的锁定机制。会话状态模块能够判断请求需要的是会话状态的只读访问还是可读/写访问,根据判断结果,它会调用getitem或getitemexclusive。在这两个方法的实现中,提供程序的编写者应创建读取会话/写入会话的锁定机制,允许多个并发的读取操作,但要阻止向已锁定的会话写入数据。
另一个问题是,要使会话状态模块知道给定会话的过期时间。若global.asax定义了session_end事件的处理程序,会话状态模块会调用setitemexpirecallback。通过该方法,状态提供程序会得到一个回调函数,其原型如下:
public delegate void sessionstateitemexpirecallback(string sessionid, sessionstatestoredata item);
会话模块要将返回的委托在内部存储,在给定会话超时时调用。对过期回调的支持是可选的,事实上,只有inproc真正支持它。如果不希望自定义提供程序支持过期回调,应指示setitemexpirecallback方法返回false。
如果要支持无cookie会话的提供程序,还必须实现createuninitialized方法,以便向数据存储中写入空的会话项。确切地讲,空的会话项是一种完整的数据项,但不包含实际的数据。也就是说,会话项应包含会话id和创建时间(可能还包含锁的id),但不包含数据。对于asp.net 2.0,在无cookie模式下,只要在会话过期后发出请求,就会生成新的id。会话状态模式会生成新的id,并使浏览器重定向。若未初始化的会话项没有被分配新的id,那么新的请求仍会被看作是过期会话中的请求。
会话数据字典的替换
sessionstatestoredata是代表会话项的类(一种包含与会话有关的所有数据的数据结构)。事实上,getitem和getitemexclusive返回的就是该类的实例。该类有3个属性:items、staticobjects和tiemout。
items最终用于为页面对象session属性中的键/值集合提供数据。staticobjects中包含的是隶属于当前会话的静态对象(如在global.asax中声明,且在会话范围中可见的对象)。timeout是会话状态的有效时间(以分为单位),默认值得为20分钟。
一旦会话状态模块为请求获得会话状态,该模块会将items集合的内容写入https教程essionstatecontainer类的新实例。该对象之后被传给httpsessionstate类的构造函数,成为session属性幕后的数据容器。
对于会话模块和状态提供程序来说,会话项的容器只不过是实现isessionstateitemcollection接口的类。默认情况下,实例使用的类为sessionstateitemcollection。只要自定义的类实现了上述接口,就可以替代sessionstateitemcollection类。
若要编写状态提供程序,sessionstateutility类会非常有用,该类包含序列化和反序列化会话项的方法,以便将其存储到某介质中,或将其读出。此外,该类还有有些方法,可以从会话中抽取数据字典并将其添加到http上下文和session属性。
自定义会话状态提供程序的注册
为使应用程序能够使用自定义的会话状态提供程序,我们需要在web.config文件中注册它。假设有一个叫samplesessionstaeprovider的提供程序类,被编译为mylib程序集:
customprovider="samplesessionprovider">
type="samplesessionstateprovider, mylib" />
自定义会话id的生成
为生成会话id,asp.net 2.0使用了名为sessionidmanager的组件。该类既不是http模块也不是提供程序,它只是一个继承于system.object且实现isessionidmanager接口的类。只要实现isessionidmanager接口,自定义的类就可以替换该组件。
默认的行为
默认的会话id模块以字节数组的形式生成会话id,并带有15个值的强随机序列进行加密。该数据之后会被编码为包含24个字符的字符串,且每个字符都符合url的标准,系统会将其用途会话id。
会话id可通过http cookie或修改的url在服务器与客户端间传输,具体方式取决于中cookieless属性的值。注意,若使用无cookie会话,会话id模块要负责将id添加到url中,并对浏览器进行重定向。默认的生成器会将浏览器重定向到下面的虚url:
http://www.contoso.com/test/(s(session_id))/page.aspx
这种url发出的请求如何被正确处理呢?在无cookie会话情况下,会话id模块会利用一个轻型的isapi筛选器(aspnet_filter.dll)将传入的url动态重写为实现资源的url。请求会被正确处理,但地址栏中的路径不会改变。检测到的会话id会被置于一个叫aspfiltersessionid的标头中。
自制的会话id管理器
会话id管理器是一个实现isessionidmanager接口的类,因而我们有两种方案来构建它:
1. 新建一个类,并使其实现该接口。
2. 从sessionidmanager派生出一个类,并重写其中的两个虚方法(createsessionid和validate),以实现某些特殊的逻辑。
isessionidmanager接口的方法见下表:
如果打算使用完全自定义的会话id生成器,应注意以下几点:
1. 生成id的算法非常关键。如果不实现强随机加密过程,当某些会话处于活动状态时,恶意用户可能会猜到有效的id。为此,生成全局唯标识符(guid)是一个不错的选择。
2. 我们可以选择是否支持无cookie的会话。如果添加这种支持,则必须使该组件能从http请求中抽取会话id,并对浏览器进行重定向。为此,可能需要isapi筛选器或http模块,对请求进行预处理并对其做适当的修改。
创建好会话id模块后,便可将其在配置文件中注册:
使用
会话状态模块负责管理所有这些任务的执行。为此,它还需要利用两个组件:会话id生成器和会话状态提供程序。在asp.net 2.0和更高版本中,二者可以由自定义的组件代替。
会话的标识每个活动的asp.net会话由15个字节(120位)的字符串标识,其中只包含符合url标准的字符。会话id是随机生成的,且是唯一的,以避免恶意攻击和数据冲突。通过某种算法从一个现有的id推算出一个有效的会话id几乎是不可能的。会话id的生成会话id的长度为15个字节,由“随机数字生成器(rng)”密码提供程序生成。该服务提供程序能返回15个随机生成的数字序列,这个数字数组之后被映射为有效的url字符,并以字符串的形式返回。如果会话中未包含任何数据,那么每个请求都会获得新生成的会话id,但会话状态不会由状态提供程序保存。然而,如果设置了session_start事件的处理程序,会话状态总会被保存(即使它为空)。因此,定义session_start事件的处理程序应谨慎,不到万不得已不要这么做,特别是在不使用进程内会话提供程序时。相反,如果是非空会话字典,那么在其超时或被放弃后,会话id仍会被保留。根据设计,即使会话状态已过时,会话id仍会持续到浏览器会话结束。这意味着,只要浏览器实例不变,同一个会话id就会用于表示多个同时存在的会话。会话cookie在浏览器与服务器之间,sessionid字符串以两种方式传递:使用cookie,或使用修改的url。默认情况下,会话状态模块会在客户端创建http cookie,但修改的url可以嵌入sessionid字符串(这种方式特别适合无cookie浏览器的情况)采用哪种方法取决于存储在web.config文件中的设置,默认会采用cookie方案。实际上,cookie无非是一种位于客户端硬盘上与web页面关联的文本文件。在asp.net中,cookie由httpcookie类实例表示。通常,cookie数据保护名称、值的集合和过期时间。此外,我们还可配置cookie的虚拟路径,还可选择是否通过安全连接(如https)传输。asp.net 2.0及更高版本利用了浏览器http-only的会话cookie支持。安装ie 6.0 sp1和winxp sp2的计算机都支持。http-only功能能够防止客户端脚本使用cookie,从而降低了为窃取会话id来进行跨站点脚本攻击的潜在危险。若启用cookie,会话状态模块会创建一个带有特定名称的cookie,并将会话id存入其中。该cookie的创建过程如下:httpcookie sessioncookie;
sessioncookie = new httpcookie("asp.net_sessionid", sessionid);
sessioncookie.path = "/";asp.net_sessionid是这个cookie的名称,sessionid字符是值。cookie还与当前域的根关联。path属性用于描述cookie的相对url。会话cookie会被分配一段非常短暂的过期时间,并在每个请求成功地结束后更新。cookie对象的expires属性用于指示客户端cookie过期的天数。如果没有显式的设置,与会话cookie一样,expires属性默认为datetime.minvalue,即.net中定义的最短时间。要写入cookie的服务器端模块,需要向response.cookies集合添加httpcookie对象。在客户端发现的所有与被请求域关联的cookie都会被上传到服务器,随后可通过request.cookies集合读取。无cookie会话为使会话状态正常工作,客户端必须能够将会话id上传至服务器端应用程序。这个过程具体细节取决于应用程序的配置。asp.net应用程序通过配置文件的定义会话特有的设置。会确定cookie的支持方式,我们需要将cookieless属性设置为下表中的某个值,这些值为httpcookiemode枚举类型: 在禁用cookie支持的情况下,假设用户通过这样的url来请求某个页面:http://www.contoso.com/test/sessions.aspx浏览器地址栏中显示的地址会发生一些变化的(其中包含会话id),如下所示:http://www.contoso.com/test/(s(sylgasdfueoruikfjoiueriljk))/sessions.aspx当会话状态模块实例化时,它会检查cookiesless属性值,如果发现cookie被禁用,请求会被重定向到一个被修改的虚拟url(http状态码为302),其中包含一个会话id,刚好在页面名的前面。当再次开始处理请求时,这个会话id会嵌入请求中。这个请求会由一个特殊的isapi筛选器(aspnet_filter.exe组件)做预处理--解析其url,若带有会话id,则将其重写为正确的url。被检测到的id会存储在单独的http标头(aspfiltersessionid)中,以便稍后使用。无cookie会话带来的问题当会话开始时,不论用户发出的是否为应用程序页面的绝对url,无cookie都会引发重定向。若使用cookie,且在地址栏填入另一个应用程序的地址,那么在返回之前的页面时,获取的是相同的会话值。如果禁用cookie,当页面回发时会自动通过相对url来实现,不受影响。但如果使用绝对url链接,则会造成会话数据则会丢失,这种情况下,总会创建新的会话。例如下面的代码就会打断当前会话:有什么办法能够自动修改链接和超链接中的绝对url,使其融入会话id信息?我们可以使用httpresponse类的applyapppathmodifier方法:applyapppathmodifier方法接受一个代表相对url的字符串,返回的是带有会话信息的绝对url。这个技巧非常适合将http页面重定向到https页面,这时必须使用完整的绝对地址。注意,如果会话cookie是启用的,或传入的路径是绝对路,那么applyapppathmodifier返回的则是原始的url。我们不能在服务器端表达式(即,带有runat=server属性的表达式)中使用<%...%>代码块。该代码块之所以能在上述代码工作是因为标签是纯文本,没有runat属性。注意,这里所说的代码块与数据绑定表达式<%#...%>没有任何关系。之所以不能在服务器端表达式中使用<%...%>代码块,是因为runat属性会指示将当前标签强制创建为服务器对象,而服务器对象不会处理这种代码块。无cookie会话与安全使用无cookie会话引发的另一个问题与安全有关。会话劫持(session hijacking)是最常见的攻击类型之一,它通过生成另一个合法用户的会话id来访问外部系统。为理解这种攻击方式,可以这样做:将应用程序配置为不使用cookie,并访问某个页面,获取带有会话id的url(可取自浏览器的地址栏),并立即通过电子邮件发送给一个您的朋友。让对方将该url粘贴到自己浏览器的地址栏中,并单击“转到”。只要您的会话还是活动的,那么您的朋友也能访问相同的会话。出于对系统安全的考虑,生成随机id很关键,因为这会使攻击者很难猜测到有效的会话id。对于无cookie会话,会话id暴露在地址栏中,对外界可见。因此,如果要将私人或敏感信息存储在会话中,建议使用安全嵌套字层(secure sockets layer,ssl)或传输层安全性(transport layer security,tls)对浏览器和服务器间包含会话id的通信进行加密。此外,若用户认为这样会降低安全性,我们还应为其提供注销功能,并调用abandon方法。这样会缩短在某个攻击者设法找到合法用户的会话id的时间。而且从安全性角度来讲,在使用无cookie会话时,有必要对应用程序进行配置,使其避免重复使用过期的会话id。在asp.net中,该行为可通过区段进行配置。 会话状态的配置在asp.net 1.x向asp.net 2.0过渡的过程中,区段的选项也随之增加,如下所示: mode="off|inproc|stateserver|sqlserver|custom"
timeout="number of minutes"
cookiename="session cookie name"
cookieless="http cookie mode"
regenerateexpiredsessionid="true|false"
sqlconnectionstring="sql connection string"
sqlcommandtimeout="number of seconds"
allowcustomsqldatabase="true|false"
usehostingidentity="true|false"
partitionresolvertype=""
sessionidmanagertype="custom session id generator"
stateconnectionstring="tcpip=server:port"
statenewworktimeout="number of seconds"
customprovider="custom provider name">
...
sessionstate的属性说明见下表:此外,子区段用户设置所有自定义会话状态存储提供程序。 会话的生存期会话状态的生存期起始于首个数据项被添加到内存中的字典时。该字典是一个sessiondictionary的内部类实例。session_start事件会话启动事件与会话状态无关。session_start事件将在会话状态模块为用户的首个请求提供服务且需要新会话id时引发。asp.net运行库能在单个会话上下文中为多个请求服务,但只有第一个请求会引发session_start事件。若不向字典中写入数据,便会在请求页面时创建新的会话id并引发session_start事件。session_end事件session_end事件用于通知会话的结束,并执行终止会话涉及的清理代码。但应注意,该属性要求当前处于inproc模式下(会话数据存储在asp.net工作线程)。为使session_end引发,会话状态必须事先已经存在。这意味着,我们必须在会话状态中存储一些数据,且至少完成一个请求。当第一个值被添加到会话字典中时,会有一个对应项被插入asp.net缓存。该行为针对的是进程内状态提供程序,进程外状态服务器和sql server状态服务都不涉及cache对象。添加到缓存的会话状态项会被指定一个可调的过期时间(会话超时设置中的间隔时间),只要有请求在当前会话上下文中处理,可调的时间会自动更新。会话状态模块会在处理endrequest事件时重置这个超时设置。该模块只要对缓存执行一次读取操作,就能达到期望的效果。asp.net cache对象的内部结构,使其能够估算出该可调时间段的长短。因此,当缓存项过期时,会话状态也已超时。过期项会自动从缓存中移除。作为过期策略的一部分,会话状态模块还会指定一个移除回调函数。缓存对象会自动调用该移除函数,而该函数会引发session_end事件。cache中代表会话状态的项无法在system.web程序集之外访问,更不能进行枚举,因为它们被置于缓存的系统保留区域内。也就是说,我们不能以编程的方式访问位于另一个会话中的数据,移除就更谈不上了。为什么会话状态有时会丢失session对象中的值可以编程方式移除,也会在会话超时或被放弃时由系统移除。但在某些情况下,会话状态会莫名其妙的丢失,这是为什么呢?当处在inproc模式下时,会话状态会被映射到处理当前请求的appdomain内存空间中。因而,会话状态会受进程回收和appdomain重启的影响。asp.net工作线程会周期性的重启以便保持总体上的良好性能,重启后,会话状态便会丢失。进程回收的执行会根据内存的占用率和被服务的请求数。虽然该过程是周期性的,但无法估计出回收的间隔。因此,在设计基于会话的、进程内的应用程序时,应充分考虑这个问题。会话状态可能在试图访问时不存在。使用异常处理,还是使用恢复技术,要根据具体的应用程序来分析。当某页面运行出现错误时,会话状态会受到怎样的影响?当前的会话字典是会被保存还是丢失?若页面在请求结束时发生错误(server对象的getlasterror方法返回一个异常对象),会话状态则不会被保存。然而,如果在异常处理程序中通过调用server.clearerror方法来重置异常状态,那么会话状态数据便会按常规方式保存,就像没有发生错误一样。将会话状态保存在远程服务器中对于前面提到的inproc模式下会话状态会丢失的问题,可以利用进程外状态提供程序来解决。但如果这样的话,会话状态不存储在asp.net工作线程中,需要一层额外的代码来对存储介质中的数据进行序列化和反序列化,该操作发生在请求被处理期间。将会话数据从外部存储区复制到本地会话字典中,可能造成状态管理进程的性能下降15%--25%。若选择进程外状态提供程序,应考虑在应用程序进入生产环境前事先建立运行时环境。这涉及启用有关stateserver的windows服务或配置sqlserver数据库教程。状态的序列化和反序列化若选择inproc模式,存储在会话状态中的对象是类的实例,无需执行序列化或反序列化。我们可以在会话状态中存储开发者所创建的任何对象,访问它们也不会有额外的开销。但如果选择进行外状态提供程序,情况则迥然不同。在进程外架构中,会话值需要从原始的存储介质复制到处理请求的appdomain的内存中。这方面的工作需要序列化/反序列化来完成,这是进程外状态提供程序主要的开销之一。这么我们的代码有何影响?首先,我们应确保字典中只存储可序列化的对象。在序列化和反序列化方面,asp.net中有两种方式,性能各有不同。对于基本的类型,asp.net会使用经优化的内部序列化程序;而对于其他类型,asp.net会使用.net二进制格式化程序,其速度较慢。经优化的序列化程序(一个叫altserialization的内部类)会使用binarywriter对象,先写入代表相应“类型”的字节,然后才是它的值。在读取时,altserialization类首先会抽取一个字节,判断要读取的数据的类型,然后借助binaryreader类中类型特定的方法进行读取具体的值。每个类型会根据一个内部的表与一个索引值关联。布尔类型和数据类型的大小是固定的,而字符串的长度却是可变的。那么,读取器是如何确定字符串的长度呢?binaryreader.readstring方法利用了这样一个技巧:在底层流中,字符串总带有一个长度前缀。对于datatime类型,该方法会通过日期和时间的总刻度数的形式写入,作为int64类型读取。对于复杂对象,只要它被标记为“可序列化”,则会通过binaryformatter类对其进行序列化,其速度相对较慢。简单类型和复杂类型都会使用同一个流,但所有非基本类型由同一个类型id来标识。我们不应该将任何对象都存储在session中。如果使用进程外方案,对dataset的存储应该谨慎。这与dataset类的序列化过程有关。由于dataset是复杂类型,它是通过二进制格式化程序进行序列化的。而dataset本身的序列化会生成许多xml数据,而这会对应用程序造成严重的缺陷,尤其对于存储大量数据的大型应用程序。会话数据的存储若工作在stateserver模式下,整个httpsessionstate对象的内容会被序列化到一个外部应用程序,即一个名为aspnet_state.exe的microsoft windows nt服务。该服务用于在请求结束时对会话状态进行序列化。在内部,该服务使用字节数组来存储每个会话状态。若开始处理新的请求,与给定会话id对应的数组会被复制到内存流中,然后被反序列化成内部的会话状态项对象。该对象代表整个会话的内容。页面实际使用的httpsessionstae对象只是其应用程序编程接口。stateserver提供程序的配置若采用进程外存储方案,会话状态的生存期便会被延迟。这样一来,应用程序的健壮性更强。通过将会话状态与页面分离,我们还能够将现有的应用程序逐步向web farm和web garden架构转化。asp.net会话状态提供程序是一个叫aspnet_state.exe的windows服务,该服务的可执行文件位于asp.net的安装文件夹:%windows%microsoft.netframework[version]。注意,具体的路径取决于实际运行的.net framework的版本。在使用状态服务前,应确保该服务在本地或用于存储会话的远程计算机上正常运行。我们需要为asp.net应用程序指定运行会话状态服务的计算机的ip地址。为启用远程会话状态,我们需要在web.config中进行配置:
stateconnectionstring="tcpip=mymachine:42424" />
服务器名既可以是ip地址,也可以是计算机名称。状态服务器不会为请求者设置任何身份验证障碍,这样,网络用户可以自由访问会话数据。为保护会话状态,应确保其只能接受来自web服务器计算机的访问。为此,我们可以使用防火墙、ipsec策略或安全网络10.x.x.x,这样,外部攻击都便不能直接访问了。我们还可以修改其服务端口,修改注册表中的键值“hkey_local_machinesystemcurrentcontrolsetservicesaspnet_stateparameters”即可修改状态服务的端口。asp.net应用程序会在加载后立即尝试连接到会话状态服务器。状态服务会使用.net remoting进行数据通信。默认情况下,状态服务器只监听本地连接,如果状态服务器和web服务器是不同的计算机,我们需要启用远程连接。为此,我们只要在注册表中修改键值“hkey_local_machinesystemcurrentcontrolsetservicesaspnet_stateallowremoteconnection”,将其设置一个非0值即可。将数据保存到sql server如果应用程序对健壮性要求较高,比如状态提供服务被停用后,数据仍然不会丢失,不妨将sql server服务。若asp.net工作在sql server模式下,会话数据存储在一个专用的数据库表中,因此,即使sql server崩溃,会话数据也不会丢失,但这需要更高的系统开销。为将sql server作为状态提供程序,需要对web.config文件进行配置:
sqlconnectionstring="server=127.0.0.1;integrated security=sspi;" />
如果不通过allowcustomsqldatabase启用自定义数据库,那么该连接字符串中不能包含database和initial catalog这样的设置。仅当allowcustomsqldatabase设置被启用时,我们才可通过database和initial catalog指定数据库名称。连接字符串也可以引用定义在区段中的连接字符串,连接字符串名称可以在 相应属性中指定。 对数据库的访问凭据,我们可以通过用户id和密码来提供,也可以利用“集成安全性”。不论使用哪个帐户来访问sql server中的会话状态,都应确保用户至少拥有db_datareader和db_datawriter权限。还应注意,为配置存储会话状态的sql server环境,需要管理员权限,因为要创建新的数据库和存储过程。对于sql server模式下的会话状态,我们能指定自定义命令的超时值(以秒为单位)sqlcommandtimeout属性来进行设置。sql server数据库的创建asp.net提供了两对用于配置数据库环境的脚本,以便创建必要的表、存储过程、触发器、作业等。第一对脚本为installsqlstate.sql和uninstallsqlstate.sql。它们能够创建一个aspstate数据和几个存储过程,但数据存储在tempdb数据库的几个表中。这样,如果sql server所处的计算机被重启,会话数据将丢失。另一对脚本为installperisistsqlstate.sql和uninstallperisistsqlstate.sql。与第一对脚本的不同之处是,它们创建的表在aspstate数据库中,是持久性的。共有两个表,表名分别为aspstatetempapplications和aspstatetempsessions。所有脚本可以在以下路径中找到:%systemroot%microsoft.netframework[version]包含这些脚本文件只是为了向后兼容,我们应使用aspnet_regsql.exe来安装和卸载sql会话状态。当前运行的每个asp.net应用程序对应于aspstatetempapplications表中的一条记录。下表对表中各列做了说明:aspstatetempsessions表用于存储实际的会话数据,每个活动的会话对应于表中的一条记录,下表描述了该表的结构:在安装会话的sql server支持时,还有一个作业被添加,它用于从会话状态数据库中删除过期的会话。该作业名称为aspstate_job_deleteexpiredsessions,默认配置是每分钟运行一次。
相关文章
- 王者荣耀侦探能力大测试攻略 王者荣耀侦探能力大测试怎么过 11-22
- 无期迷途主线前瞻兑换码是什么 11-22
- 原神欧洛伦怎么培养 11-22
- 炉石传说网易云音乐联动怎么玩 11-22
- 永劫无间手游确幸转盘怎么样 11-22
- 无期迷途主线前瞻兑换码是什么 无期迷途主线前瞻直播兑换码介绍 11-22