Q
A:
好象可以直接传递?
HyperLink1.NavigateUrl = "Forum.aspx?a=" + "中文";
也可以这样解码传递
string urlFmt = "Forum.aspx?a={0}";
HyperLink1.NavigateUrl = string.Format(urlFmt, Server.UrlEncode("测试中文"));
Q :Response.Flush()
Flush 方法立即发送缓冲区中的输出。如果未将 Response.Buffer 设置为 TRUE,则该方法将导致运行时错误。
语法
Response.Flush
注释
如果在 ASP 页上调用 Flush 方法,则服务器将响应该页上保持活动的请求。
Response.Flush
对于响应缓冲,因为用户在看到东西之前必须等待整个页面生成,所以用户可能够感觉到ASP页面响应比较慢(虽然整体响应时间缩短了);对一个运行时间较长的页面,可以同过Response.Buffer = False 来关掉响应缓冲;但更好的策略是使用
Reponse.Flush方法。这个方法把所有已经由ASP生成的HTML输出到浏览器中。例
如,一个1,000行的大表,在写完100行之后,ASP可以调用Response.Flush来强制把结果写到浏览器中,这样,用户就可以在其余行生成之前先看到100行数据。这个技术能让你两全其美—响应缓冲和渐进式地在浏览器表现数据。
(注意,在上面的1,000行表的例子中,很多浏览器在遇到</table>标记之前可能并不画出整个表。如果想让浏览器逐步显示出数据,可以将一个大表分成多个小表,然后对每个小表调用Response.Flush。新版本的IE会在下载完整个表之前显示表,并且如果指定了表的列宽,显示的速度会更快。)
另外,当产生一个非常大的页面时,响应缓冲可能会消耗掉许多的服务器内存。这个问题也可以通过使用Response.Flush来解决。
Flush的内容至少要有256字节
很多时候我们写的asp程序会因为做很多操作,所以会花上一分钟甚至几分钟时间。为了使软件使用者能够耐心的等待程序的执行,我们经常会希望有一个进度条来表示程序执行的状态。或者最起码要显示一个类似: “数据载入中”,“正在保存数据” 等的说明性文字。此时我们就会用到Response.flush()。他会将缓冲区中编译完成的数据先发送到客户端。
但是有很多时候,我们发现即使我们使用了Response.Flush(),但是并没有将前面的信息发到客户端来显示。呈献给我们的依然是白屏。经过反复的测试,我得出一个结论(仅代表个人观点,可随意引用,但后果自负)。就是flush的内容至少要有256字节。也就是只有编译产生了至少256字节的数据,才能在执行Response.Flush()以后将信息发到客户端并显示。
轻松加密ASP.NET 2.0 Web程序配置信息
一、 简介
当创建ASP.NET 2.0应用程序时,开发者通常都把敏感的配置信息存储在Web.config文件中。最典型的示例就是数据库连接字符串,但是包括在Web.config文件中的其它敏感信息还包括SMTP服务器连接信息和用户凭证数据,等等。 尽管默认情况下可以配置ASP.NET以拒绝所有对扩展名为.config的文件资源的HTTP请求;但是,如果一个黑客能够存取你的web服务器的文件系统的话,那么,Web.config中的敏感信息仍然能够被窃取。例如,也许你不小心允许匿名FTP存取你的网站,这样以来就允许一个黑客简单地通过FTP协议下载你的Web.config文件。
幸好,通过允许加密Web.config文件中选择的部分,例如<connectionStrings>节,或你的应用程序使用的一些定制config节,ASP.NET 2.0有助于缓解这个问题。配置部分能够很容易地使用编码或aspnet_regiis.exe(一个命令行程序)预以加密。一旦被加密,Web.config设置即可避开"虎视眈眈"的眼睛。而且,当以编程方式从你的ASP.NET页面中检索加密的配置设置时,ASP.NET会自动地解密它读取的加密部分。简言之,一旦配置信息被加密,你就不需要在你的应用程序中编写任何其它代码或采取任何进一步的行为来使用该加密数据。
在本文中,我们将讨论如何以编程方式加密和解密该配置设置部分,并且分析一下命令行程序aspnet_regiis.exe的使用。然后,我们将评估ASP.NET 2.0提供的加密选项。另外,还会简短地讨论一下如何加密ASP.NET版本1.x中的配置信息。
二、 前提
在我们开始探讨如何加密ASP.NET 2.0配置信息之前,请记住下列几点:
1. 所有形式的加密都会包含某种秘密,而当加密和解密数据时都要使用这一秘密。对称加密算法在加密和解密一个消息时使用同一把密钥,而非对称加密算法对于加密和解密却使用不同的密钥。无论使用哪种技术,最重要的还是看解密密钥的安全保存程度。
2. ASP.NET 2.0提供的配置加密技术的设计目的在于,力图阻止能够以某种方式检索你的配置文件的黑客的入侵。其实现思想是,如果在黑客的计算机上有你的Web.config文件;那么,他不能破解该加密的部分。然而,当web服务器上的一个ASP.NET页面从一个加密的配置文件请求信息时,该数据必须被解密才能使用(并且这时不需要你编写任何代码)。因此,如果一个黑客能够把一个能够查询配置文件并显示它的结果的ASP.NET web页面上传到你的系统,那么,他就能够以普通文本方式观看被加密的设置。(详细情况请参考本文提供的示例ASP.NET页面,它展示了加密和解密Web.config文件中各部分的方法;如你所见,一个ASP.NET页面能够存取(并显示)该加密数据的普通文本形式)
3. 加密和解密配置信息需要付出一定的性能代价。因此,通常是仅加密包含敏感信息的配置部分。比如说,可能不需要加密<compilation>或<authorization>配置部分。
三、 加密何种信息
在我们分析如何加密ASP.NET 2.0配置信息前,让我们首先来看一下能够加密什么配置信息。使用.NET框架2.0提供的库,开发人员能够加密在Web.config或machine.config文件中的绝大多数的配置部分。这些配置部分是一些作为<configuration>或<system.web>元素子结点的XML元素。例如,下面的示例Web.config文件中含有三个配置设置,显式地定义为:
<connectionStrings>,<compilation>和<authentication>。 <?xml version="1.0"?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0";> <connectionStrings> <add name="MembershipConnectionString" connectionString="connectionString"/> </connectionStrings> <system.web> <compilation debug="true"/> <authentication mode="Forms" /> </system.web>
|
这些节中的每一个都可以有选择地被加密,或者通过编程方式或通过aspnet_regiis.exe(一个命令行工具)实现。当被加密时,加密后的文本直接存储在配置文件中。例如,如果我们要加密上面的<connectionStrings>节,那么结果Web.config文件可能看起来如下所示:(注意:篇幅所限,我们省略了一大块<CipherValue>)
<?xml version="1.0"?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0";> <connectionStrings configProtectionProvider="DataProtectionConfigurationProvider"> <EncryptedData> <CipherData> <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAed...GicAlQ==</CipherValue> </CipherData> </EncryptedData> </connectionStrings> <system.web> <compilation debug="true"/> <authentication mode="Forms" /> </system.web>
|
另外,存在一些你不能使用这个技术加密的配置部分:
· <processModel> · <runtime> · <mscorlib> · <startup> · <system.runtime.remoting> · <configProtectedData> · <satelliteassemblies> · <cryptographySettings> · <cryptoNameMapping> · <cryptoClasses>
为了加密这些配置部分,你必须加密这些值并把它存储在注册表中。存在一个aspnet_setreg.exe命令行工具可以帮助你实现这一过程;我们将在本文后面讨论这个工具。
【提示】Web.Config和Machine.Config之区别:
Web.config文件指定针对一个特定的web应用程序的配置设置,并且位于应用程序的根目录下;而machine.config文件指定所有的位于该web服务器上的站点的配置设置,并且位于$WINDOWSDIR$/Microsoft.Net/Framework/Version/CONFIG目录下。
四、加密选项
开发人员可以使用ASP.NET 2.0提供程序模型来保护配置节信息,这允许任何实现都可以被无缝地插入到该API中。.NET框架2.0中提供了两个内置的提供程序用于保护配置节信息:
· Windows数据保护API(DPAPI)提供程序(DataProtectionConfigurationProvider):这个提供程序使用Windows内置的密码学技术来加解密配置节。默认情况下,这个提供程序使用本机的密钥。你还能够使用用户密钥,但是这要求进行一点定制。
· RSA保护的配置提供程序(RSAProtectedConfigurationProvider):使用RSA公钥加密来加解密配置节。使用这个提供程序,你需要创建存储用于加解密配置信息的公钥和私钥的密钥容器。你能够在一个多服务器场所下使用RSA,这只要创建可输出的密钥容器即可。 当然,如果需要的话,你还能够创建自己的保护设置提供程序。
在本文中,我们仅讨论使用DPAPI提供程序使用机器级密钥。到目前为止,这是最简单的方法,因为它不请求创建任何密钥或密钥容器。当然,其消极的一面在于:一个加密的配置文件仅能够用于首先实现加密的web服务器上;而且,使用机器密钥将允许加密的文本能够被web服务器上的任何网站所解密。
五、以编程方式加密配置部分
System.Configuration.SectionInformation类对一个配置节的描述进行了抽象。为了加密一个配置节,只需要简单地使用SectionInformation类的ProtectSection(提供程序)方法,传递你想使用的提供程序的名字来执行加密。为了存取你的应用程序的Web.config文件中的一个特定的配置节,你可以使用WebConfigurationManager类(在System.Web.Configuration命名空间中)来引用你的Web.config文件,然后使用它的GetSection(sectionName)方法返回一个ConfigurationSection实例。最后,你可以经由ConfigurationSection实例的SectionInformation属性得到一个SectionInformation对象。
下面,我们通过一个简单的代码示例来说明问题:
privatevoid ProtectSection(string sectionName, string provider) { Configuration config = WebConfigurationManager. OpenWebConfiguration(Request.ApplicationPath); ConfigurationSection section = config.GetSection(sectionName); if (section != null &&!section.SectionInformation.IsProtected) { section.SectionInformation.ProtectSection(provider); config.Save(); } } private void UnProtectSection(string sectionName) { Configuration config =WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath); ConfigurationSection section = config.GetSectio n(sectionName); if (section != null && section.SectionInformation.IsProtected) { section.SectionInformation.UnprotectSection(); config.Save(); }
|
你可以从一个ASP.NET页面中调用这个ProtectSection(sectionName,provider)方法,其相应的参数是一个节名(如connectionStrings)和一个提供程序(如DataProtectionConfigurationProvider),并且它打开Web.config文件,引用该节,调用SectionInformation对象的ProtectSection(provider)方法,最后保存配置变化。
另一方面,UnProtectSection(provider)方法实现解密一个特定的配置节。在此,仅需要传入要解密的节-我们不需要麻烦提供程序,因为该信息已经存储在伴随encrypted节的标记中(也即是,在上面的示例中的<connectionStrings>节,在被加密以后,它包含了提供程序:<connectionStringsconfigProtectionProvider="DataProtectionConfigurationProvider">)。
记住,一旦该数据被加密,当从一个ASP.NET页面读取它时(也即是,从一个SqlDataSource控件或以编程方式经由ConfigurationManager.ConnectionStrings[connStringName].ConnectionString读取该连接字符串信息),ASP.NET会自动地解密该连接字符串并且返回普通文本值。换句话说,在实现加密后,你一点不需要改变你的代码。相当酷,对不对?
从本文下载的示例ASP.NET 2.0网站中,你会发现有一个示例页面,它展示了该站点的Web.config文件,其中有一个多行TextBox,还提供了相应的Web控件按钮用于加密配置文件的各个部分。这个示例中也使用了上面已经讨论过的ProtectSection()和UnProtectSection()方法。
六、 使用命令行工具aspnet_regiis.exe
你还能够使用aspnet_regiis.exe命令行工具来加密和解密Web.config文件配置部分,你可以在"%WINDOWSDIR%/Microsoft.Net/Framework/version"目录下找到这个工具。为了加密Web.config文件中的一个节,你可以在这个命令行工具中使用DPAPI机器密钥,如下所示:
加密一个特定网站的Web.config文件的通用形式:
aspnet_regiis.exe -pef section physical_directory -prov provider
|
或:
aspnet_regiis.exe -pe section -app virtual_directory -prov provider
|
加密一个特定网站的Web.config文件的具体实例:
aspnet_regiis.exe -pef "connectionStrings" "C:/Inetpub/wwwroot/MySite" -prov "DataProtectionConfigurationProvider"
|
或:
aspnet_regiis.exe -pe "connectionStrings" -app "/MySite" -prov "DataProtectionConfigurationProvider"
|
解密一个特定网站的Web.config文件的通用形式:
aspnet_regiis.exe -pdf section physical_directory
|
或:
aspnet_regiis.exe -pd section -app virtual_directory
|
解密一个特定网站的Web.config文件的具体实例:
aspnet_regiis.exe -pdf "connectionStrings" "C:/Inetpub/wwwroot/MySite"
|
或:
你还能够指定由aspnet_regiis.exe来执行machine.config文件的加密/解密。
【提示】 加密ASP.NET版本1.x中的配置设置
为了保护ASP.NET版本1.x中的配置设置,开发者需要加密并把敏感的设置存储在web服务器的注册表中,并以一种"强"键方式存储。配置文件中不是存储加密的内容(如ASP.NET 2.0那样),而只是包含一个到存储该加密值的注册表键的引用。例如:
<identity impersonate="true" userName="registry:HKLM/SOFTWARE/MY_SECURE_APP/identity/ASPNET_SETREG,userName" password="registry:HKLM/SOFTWARE/MY_SECURE_APP/identity/ASPNET_SETREG,password" />
|
微软为开发人员提供了aspnet_setreg.exe命令行工具,用于加密敏感的配置信息并且把它移动到一个"强"注册表入口处。遗憾的是,这个工具仅针对特定的配置设置工作;相比之下,ASP.NET 2.0允许加密任何配置节。
有关于在一个ASP.NET 1.x应用程序中使用aspnet_setreg.exe的更多信息请参考MSDN中的KB#32990。遗憾的是,这个命令行程序仅能加密配置设置中的预定义的节,并且不允许你加密你自己添加的数据库连接字符串和其它敏感信息。
七、 结论
在本文中,我们学习了如何使用ASP.NET 2.0提供的不同的加密选项来保护配置节信息,还讨论了如何使用编程技术和aspnet_regiis.exe来分别加密Web.config中的配置节。保护你的敏感的配置设置有助于确保你的站点更难于被黑客攻击-通过使其更难于发现敏感的配置设置。如今,ASP.NET 2.0已经提供了相对容易的加密和解密技术,开发者毫无理由不使用这种方式来保护你的敏感的配置设置。
|
Gridview中的内容导出到Excel
protected void Button1_Click(object sender, System.EventArgs e)
{
Response.Clear();
Response.AddHeader("content-disposition", "attachment;filename=FileName.xls");
Response.Charset = "gb2312";
Response.ContentType = "application/vnd.xls";
System.IO.StringWriter stringWrite = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
GridView1.AllowPaging = false;
iniData();
GridView1.RenderControl(htmlWrite);
Response.Write(stringWrite.ToString());
Response.Flush();
Response.End();
GridView1.AllowPaging = true;
iniData();
}
public override void VerifyRenderingInServerForm(System.Web.UI.Control control)
{
//Confirms that an HtmlForm control is rendered for
}
在上面的代码中,我们首先将gridview绑定到指定的数据源中,然后在button1的按钮(用来做导出到EXCEL的)的事件中,写入相关的代码。这里使用Response.AddHeader("content-disposition","attachment;filename=exporttoexcel.xls");中的filename来指定将要导出的excel的文件名,这里是exporttoexcel.xls。要注意的是,由于gridview的内容可能是分页显示的,因此,这里在每次导出excel时,先将gridview的allowpaging属性设置为false,然后通过页面流的方式导出当前页的gridview到excel中,最后再重新设置其allowpaging属性。另外要注意的是,要写一个空的VerifyRenderingInServerForm方法(必须写),以确认在运行时为指定的ASP.NET 服务器控件呈现HtmlForm 控件。
编写高性能Web应用程序的10个入门技巧
使用 ASP.NET 编写 Web 应用程序的简单程度令人不敢相信。正因为如此简单,所以很多开发人员就不会花时间来设计其应用程序的结构,以获得更好的性能了。在本文中,我将讲述 10 个用于编写高性能 Web 应用程序的技巧。但是我并不会将这些建议仅局限于ASP.NET 应用程序,因为这些应用程序只是 Web 应用程序的一部分。本文不作为对 Web应用程序进行性能调整的权威性指南 — 一整本书恐怕都无法轻松讲清楚这个问题。请将本文视作一个很好的起点。
成为工作狂之前,我原来喜欢攀岩。在进行任何大型攀岩活动之前,我都会首先仔细查看指南中的路线,阅读以前游客提出的建议。但是,无论指南怎么好,您都需要真正的攀岩体验,然后才能尝试一个特别具有挑战性的攀登。与之相似,当您面临修复性能问题或者运行一个高吞吐量站点的问题时,您只能学习如何编写高性能 Web 应用程序。
我的个人体验来自在 Microsoft 的 ASP.NET 部门作为基础架构程序经理的经验,在此期间我运行和管理 www.ASP.NET,帮助设计社区服务器的结构,社区服务器是几个著名 ASP.NET 应用程序(组合到一个平台的 ASP.NET Forums、.Text 和 nGallery)。我确信有些曾经帮助过我的技巧对您肯定也会有所帮助。
您应该考虑将应用程序分为几个逻辑层。您可能听说过 3 层(或者 n 层)物理体系结构一词。这些通常都是规定好的体系结构方式,将功能在进程和/或硬件之间进行了物理分离。当系统需要扩大时,可以很轻松地添加更多的硬件。但是会出现一个与进程和机器跳跃相关的性能下降,因此应该避免。所以,如果可能的话,请尽量在同一个应用程序中一起运行 ASP.NET 页及其相关组件。
因为代码分离以及层之间的边界,所以使用 Web 服务或远程处理将会使得性能下降 20% 甚至更多。
数据层有点与众不同,因为通常情况下,最好具有专用于数据库的硬件。然而进程跳跃到数据库的成本依然很高,因此数据层的性能是您在优化代码时首先要考虑的问题。
在深入应用程序的性能修复问题之前,请首先确保对应用程序进行剖析,以便找出具体的问题所在。主要性能计数器(如表示执行垃圾回收所需时间百分比的计数器)对于找出应用程序在哪些位置花费了其主要时间也非常有用。然而花费时间的位置通常非常不直观。
本文讲述了两种类型的性能改善:大型优化(如使用 ASP.NET 缓存),和进行自身重复的小型优化。这些小型优化有时特别有意思。您对代码进行一点小小的更改,就会获得很多很多时间。使用大型优化,您可能会看到整体性能的较大飞跃。而使用小型优化时,对于某个特定请求可能只会节省几毫秒的时间,但是每天所有请求加起来,则可能会产生巨大的改善。
数据层性能
谈到应用程序的性能调整,有一个试纸性的测试可用来对工作进行优先级划分:代码是否访问数据库?如果是,频率是怎样的?请注意,这一相同测试也可应用于使用 Web 服务或远程处理的代码,但是本文对这些内容未做讲述。
如果某个特定的代码路径中必需进行数据库请求,并且您认为要首先优化其他领域(如字符串操作),则请停止,然后执行这个试纸性测试。如果您的性能问题不是非常严重的话,最好花一些时间来优化一下与数据库、返回的数据量、进出数据库的往返频率相关的花费时间。
了解这些常规信息之后,我们来看一下可能会有助于提高应用程序性能的十个技巧。首先,我要讲述可能会引起最大改观的更改。
技巧 1 — 返回多个结果集
仔细查看您的数据库代码,看是否存在多次进入数据库的请求路径。每个这样的往返都会降低应用程序可以提供的每秒请求数量。通过在一个数据库请求中返回多个结果集,可以节省与数据库进行通信所需的总时间长度。同时因为减少了数据库服务器管理请求的工作,还会使得系统伸缩性更强。
虽然可以使用动态 SQL 返回多个结果集,但是我首选使用存储过程。关于业务逻辑是否应该驻留于存储过程的问题还存在一些争议,但是我认为,如果存储过程中的逻辑可以约束返回数据的话(缩小数据集的大小、缩短网络上所花费时间,不必筛选逻辑层的数据),则应赞成这样做。
使用 SqlCommand 实例及其 ExecuteReader 方法填充强类型的业务类时,可以通过调用NextResult 将结果集指针向前移动。图 1 显示了使用类型类填充几个 ArrayList 的示例会话。只从数据库返回您需要的数据将进一步减少服务器上的内存分配。
Figure 1 Extracting Multiple Resultsets from a DataReader // read the first resultset reader = command.ExecuteReader();
// read the data from that resultset while (reader.Read()) { suppliers.Add(PopulateSupplierFromIDataReader( reader )); }
// read the next resultset reader.NextResult();
// read the data from that second resultset while (reader.Read()) { products.Add(PopulateProductFromIDataReader( reader )); }
技巧 2 — 分页的数据访问
ASP.NET DataGrid 具有一个很好的功能:数据分页支持。在 DataGrid 中启用分页时,一次会显示固定数量的记录。另外,在 DataGrid 的底部还会显示分页 UI,以便在记录之间进行导航。该分页 UI 使您能够在所显示的数据之间向前和向后导航,并且一次显示固定数量的记录。
还有一个小小的波折。使用 DataGrid 的分页需要所有数据均与网格进行绑定。例如,您的数据层需要返回所有数据,那么 DataGrid 就会基于当前页筛选显示的所有记录。如果通过 DataGrid 进行分页时返回了 100,000 个记录,那么针对每个请求会放弃 99,975 个记录(假设每页大小为 25 个记录)。当记录的数量不断增加时,应用程序的性能就会受到影响,因为针对每个请求必须发送越来越多的数据。
要编写性能更好的分页代码,一个极佳的方式是使用存储过程。图 2 显示了针对Northwind 数据库中的 Orders 表进行分页的一个示例存储过程。简而言之,您此时要做的只是传递页索引和页大小。然后就会计算合适的结果集,并将其返回。
Figure 2 Paging Through the Orders Table CREATE PROCEDURE northwind_OrdersPaged ( @PageIndex int, @PageSize int ) AS BEGIN DECLARE @PageLowerBound int DECLARE @PageUpperBound int DECLARE @RowsToReturn int
-- First set the rowcount SET @RowsToReturn = @PageSize * (@PageIndex + 1) SET ROWCOUNT @RowsToReturn
-- Set the page bounds SET @PageLowerBound = @PageSize * @PageIndex SET @PageUpperBound = @PageLowerBound + @PageSize + 1
-- Create a temp table to store the select results CREATE TABLE #PageIndex ( IndexId int IDENTITY (1, 1) NOT NULL, OrderID int )
-- Insert into the temp table INSERT INTO #PageIndex (OrderID) SELECT OrderID FROM Orders ORDER BY OrderID DESC
-- Return total count SELECT COUNT(OrderID) FROM Orders
-- Return paged results SELECT O.* FROM Orders O, #PageIndex PageIndex WHERE O.OrderID = PageIndex.OrderID AND PageIndex.IndexID > @PageLowerBound AND PageIndex.IndexID < @PageUpperBound ORDER BY PageIndex.IndexID
END
在社区服务器中,我们编写了一个分页服务器控件,以完成所有的数据分页。您将会看到,我使用的就是技巧 1 中讨论的理念,从一个存储过程返回两个结果集:记录的总数和请求的数据。
返回记录的总数可能会根据所执行查询的不同而有所变化。例如,WHERE 子句可用来约束返回的数据。为了计算在分页 UI 中显示的总页数,必须了解要返回记录的总数。例如,如果总共有 1,000,000 条记录,并且要使用一个 WHERE 子句将其筛选为 1000 条记录,那么分页逻辑就需要了解记录的总数才能正确呈现分页 UI。
技巧 3 — 连接池
在 Web 应用程序和 SQL Server之间设置 TCP 连接可能是一个非常消耗资源的操作。Microsoft 的开发人员到目前为止能够使用连接池已经有一段时间了,这使得他们能够重用数据库连接。他们不是针对每个请求都设置一个新的 TCP 连接,而是只在连接池中没有任何连接时才设置新连接。当连接关闭时,它会返回连接池,在其中它会保持与数据库的连接,而不是完全破坏该 TCP 连接。
当然,您需要小心是否会出现泄漏连接。当您完成使用连接时,请一定要关闭这些连接。再重复一遍:无论任何人对 Microsoft?.NET Framework 中的垃圾回收有什么评论,请一定要在完成使用连接时针对该连接显式调用 Close 或 Dispose。不要相信公共语言运行库(CLR) 会在预先确定的时间为您清除和关闭连接。尽管 CLR 最终会破坏该类,并强制连接关闭,但是当针对对象的垃圾回收真正发生时,并不能保证。
要以最优化的方式使用连接池,需要遵守一些规则。首先打开连接,执行操作,然后关闭该连接。如果您必须如此的话,可以针对每个请求多次打开和关闭连接(最好应用技巧 1),但是不要一直将连接保持打开状态并使用各种不同的方法对其进行进出传递。第二,使用相同的连接字符串(如果使用集成身份验证的话,还要使用相同的线程标识)。如果不使用相同的连接字符串,例如根据登录的用户自定义连接字符串,那么您将无法得到连接池提供的同一个优化值。如果您使用集成身份验证,同时还要模拟大量用户,连接池的效率也会大大下降。尝试跟踪与连接池相关的任何性能问题时,.NET CLR 数据性能计数器可能非常有用。
每当应用程序连接资源时,如在另一个进程中运行的数据库,您都应该重点考虑连接该资源所花时间、发送或检索数据所花时间,以及往返的数量,从而进行优化。优化应用程序中任何种类的进程跳跃都是获得更佳性能的首要一点。
应用层包含了连接数据层、将数据转换为有意义类实例和业务流程的逻辑。例如社区服务器,您要在其中填充Forums 或 Threads集合,应用业务规则(如权限);最重要的是要在其中执行缓存逻辑。
技巧 4 — ASP.NET 缓存 API
编写应用程序代码行之前,一个首要完成的操作是设计应用层的结构,以便最大化利用ASP.NET 缓存功能。
如果您的组件要在 ASP.NET 应用程序中运行,则只需在该应用程序项目中包括一个System.Web.dll 引用。当您需要访问该缓存时,请使用 HttpRuntime.Cache 属性(通过Page.Cache 和 HttpContext.Cache 也可访问这个对象)。
对于缓存数据,有几个规则。首先,如果数据可能会多次使用时,则这是使用缓存的一个很好的备选情况。第二,如果数据是通用的,而不特定于某个具体的请求或用户时,则也是使用缓存的一个很好的备选情况。如果数据是特定于用户或请求的,但是寿命较长的话,仍然可以对其进行缓存,但是这种情况可能并不经常使用。第三,一个经常被忽略的规则是,有时可能您缓存得太多。通常在一个 x86 计算机上,为了减少内存不足错误出现的机会,您会想使用不高于 800MB 的专用字节运行进程。因此缓存应该有个限度。换句话说,您可能能够重用某个计算结果,但是如果该计算采用 10 个参数的话,您可能要尝试缓存 10 个排列,这样有可能给您带来麻烦。一个要求 ASP.NET 的最常见支持是由于过度缓存引起的内存不足错误,尤其是对于大型数据集。
缓存有几个极佳的功能,您需要对它们有所了解。首先,缓存会实现最近最少使用的算法,使得 ASP.NET 能够在内存运行效率较低的情况下强制缓存清除 - 从缓存自动删除未使用过的项目。第二,缓存支持可以强制失效的过期依赖项。这些依赖项包括时间、密钥和文件。时间经常会用到,但是对于 ASP.NET 2.0,引入了一个功能更强的新失效类型:数据库缓存失效。它指的是当数据库中的数据发生变化时自动删除缓存中的项。有关数据库缓存失效的详细信息,请参阅 MSDN?Magazine 2004 年 7 月的 Dino Esposito Cutting Edge 专栏。要了解缓存的体系结构,请参阅下图。
技巧 5 — 每请求缓存
在本文前面部分,我提到了经常遍历代码路径的一些小改善可能会导致较大的整体性能收益。对于这些小改善,其中有一个绝对是我的最爱,我将其称之为"每请求缓存"。
缓存 API 的设计目的是为了将数据缓存较长的一段时间,或者缓存至满足某些条件时,但每请求缓存则意味着只将数据缓存为该请求的持续时间。对于每个请求,要经常访问某个特定的代码路径,但是数据却只需提取、应用、修改或更新一次。这听起来有些理论化,那么我们来举一个具体的示例。
在社区服务器的论坛应用程序中,页面上使用的每个服务器控件都需要个性化的数据来确定使用什么外观、使用什么样式表,以及其他个性化数据。这些数据中有些可以长期缓存,但是有些数据却只针对每个请求提取一次,然后在执行该请求期间对其重用多次,如要用于控件的外观。
为了达到每请求缓存,请使用 ASP.NET HttpContext。对于每个请求,都会创建一个HttpContext 实例,在该请求期间从 HttpContext.Current 属性的任何位置都可访问该实例。该 HttpContext 类具有一个特殊的 Items 集合属性;添加到此 Items 集合的对象和数据只在该请求持续期间内进行缓存。正如您可以使用缓存来存储经常访问的数据一样,您也可以使用 HttpContext.Items 来存储只基于每个请求使用的数据。它背后的逻辑非常简单:数据在它不存在的时候添加到 HttpContext.Items 集合,在后来的查找中,只是返回 HttpContext.Items 中的数据。
技巧 6 — 后台处理
通往代码的路径应该尽可能快速,是吗?可能有时您会觉得针对每个请求执行的或者每n 个请求执行一次的任务所需资源非常多。发送电子邮件或者分析和验证传入数据就是这样的一些例子。
剖析 ASP.NET Forums 1.0 并重新构建组成社区服务器的内容时,我们发现添加新张贴的代码路径非常慢。每次添加新张贴时,应用程序首先需要确保没有重复的张贴,然后必须使用"坏词"筛选器分析该张贴,分析张贴的字符图释,对张贴添加标记并进行索引,请求时将张贴添加到合适的队列,验证附件,最终张贴之后,立即向所有订阅者发出电子邮件通知。很清楚,这涉及很多操作。
经研究发现,大多数时间都花在了索引逻辑和发送电子邮件上。对张贴进行索引是一个非常耗时的操作,人们发现内置的 System.Web.Mail 功能要连接 SMYP 服务器,然后连续发送电子邮件。当某个特定张贴或主题领域的订阅者数量增加时,执行 AddPost 功能所需的时间也越来越长。
并不需要针对每个请求都进行电子邮件索引。理想情况下,我们想要将此操作进行批处理,一次索引 25 个张贴或者每五分钟发送一次所有电子邮件。我们决定使用以前用于对数据缓存失效进行原型设计的代码,这个失效是用于最终进入 Visual Studio? 2005 的内容的。
System.Threading 命名空间中的 Timer 类非常有用,但是在 .NET Framework 中不是很有名,至少对于 Web 开发人员来说是这样。创建之后,这个 Timer 类将以一个可配置的间隔针对 ThreadPool 中的某个线程调用指定的回调。这就表示,您可以对代码进行设置,使其能够在没有对 ASP.NET 应用程序进行传入请求的情况下得以执行,这是后台处理的理想情况。您还可以在此后台进程中执行如索引或发送电子邮件之类的操作。
但是,这一技术有几个问题。如果应用程序域卸载,该计时器实例将停止触发其事件。另外,因为 CLR 对于每个进程的线程数量具有一个硬性标准,所以可能会出现这样的情形:服务器负载很重,其中计时器可能没有可在其基础上得以完成的线程,在某种程度上可能会造成延迟。ASP.NET 通过在进程中保留一定数量的可用线程,并且仅使用总线程的一部分用于请求处理,试图将上述情况发生的机会降到最低。但是,如果您具有很多异步操作时,这可能就是一个问题了。
这里没有足够的空间来放置该代码,但是您可以下载一个可以看懂的示例,网址是www.rob-howard.net。请了解一下 Blackbelt TechEd 2004 演示中的幻灯片和演示。
技巧 7 — 页输出缓存和代理服务器
ASP.NET 是您的表示层(或者说应该是您的表示层);它由页、用户控件、服务器控件(HttpHandlers 和 HttpModules)以及它们生成的内容组成。如果您具有一个 ASP.NET 页,它会生成输出(HTML、XML、图像或任何其他数据),并且您针对每个请求运行此代码时,它都会生成相同的输出,那么您就拥有一个可用于页输出缓存的绝佳备选内容。
将此行内容添加页的最上端 <%@ Page OutputCache VaryByParams="none" Duration="60" %>
就可以高效地为此页生成一次输出,然后对它进行多次重用,时间最长为 60 秒,此时该页将重新执行,输出也将再一次添加到 ASP.NET 缓存。通过使用一些低级程序化 API 也可以完成此行为。对于输出缓存有几个可配置的设置,如刚刚讲到的 VaryByParams 属性。VaryByParams 刚好被请求到,但还允许您指定 HTTP GET 或 HTTP POST 参数来更改缓存项。例如,只需设置 VaryByParam="Report" 即可对 default.aspx?Report=1 或 default.aspx?Report=2 进行输出缓存。通过指定一个以分号分隔的列表,还可以指定其他参数。
很多人都不知道何时使用输出缓存,ASP.NET 页还会生成一些位于缓存服务器下游的HTTP 标头,如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的标头。设置了 HTTP 缓存标头之后,可以在这些网络资源上对文档进行缓存,客户端请求也可在不必返回原始服务器的情况下得以满足。
因此,使用页输出缓存不会使得您的应用程序效率更高,但是它可能会减少服务器上的负载,因为下游缓存技术会缓存文档。当然,这可能只是匿名内容;一旦它成为下游之后,您就再也不会看到这些请求,并且再也无法执行身份验证以阻止对它的访问了。
技巧 8 — 运行 IIS 6.0(只要用于内核缓存)
如果您未运行 IIS 6.0 (Windows Server? 2003),那么您就错过了 Microsoft Web 服务器中的一些很好的性能增强。在技巧 7 中,我讨论了输出缓存。在 IIS 5.0 中,请求是通过 IIS 然后进入 ASP.NET 的。涉及到缓存时,ASP.NET 中的 HttpModule 会接收该请求,并返回缓存中的内容。
如果您正在使用 IIS 6.0,就会发现一个很好的小功能,称为内核缓存,它不需要对ASP.NET 进行任何代码更改。当请求由 ASP.NET 进行输出缓存时,IIS 内核缓存会接收缓存数据的一个副本。当请求来自网络驱动程序时,内核级别的驱动程序(无上下文切换到用户模式)就会接收该请求,如果经过了缓存,则会将缓存的数据刷新到响应,然后完成执行。这就表示,当您将内核模式缓存与 IIS 和 ASP.NET 输出缓存一起使用时,就会看到令人不敢相信的性能结果。在 ASP.NET 的 Visual Studio 2005 开发过程中,我一度是负责 ASP.NET 性能的程序经理。开发人员完成具体工作,但是我要看到每天进行的所有报告。内核模式缓存结果总是最有意思的。最常见的特征是网络充满了请求/响应,而 IIS 运行时的 CPU 使用率只有大约 5%。这太令人震惊了!当然使用 IIS 6.0 还有一些其他原因,但是内核模式缓存是其中最明显的一个。
技巧 9 — 使用 Gzip 压缩
虽然使用 gzip 并不一定是服务器性能技巧(因为您可能会看到 CPU 使用率的提高),但是使用 gzip 压缩可以减少服务器发送的字节数量。这就使人们觉得页速度加快了,并且还减少了带宽的用量。根据所发送数据、可以压缩的程度以及客户端浏览器是否支持(IIS只会向支持 gzip 压缩的客户端发送经过 gzip 压缩的内容,如 Internet Explorer 6.0 和 Firefox),您的服务器每秒可以服务于更多的请求。实际上,几乎每当您减少所返回数据的数量时,都会增加每秒请求数。
Gzip 压缩已经内置到 IIS 6.0 中,并且其性能比 IIS 5.0 中使用的 gzip 压缩要好的多,这是好消息。但不幸的是,当尝试在 IIS 6.0 中打开 gzip 压缩时,您可能无法在IIS 的属性对话中找到该设置。IIS 小组在该服务器中置入了卓越的 gzip 功能,但是忘了包括一个用于启用该功能的管理 UI。要启用 gzip 压缩,您必须深入到 IIS 6.0 的 XML 配置设置内部(这样不会引起心脏虚弱)。顺便提一句,这归功于 OrcsWeb 的 Scott Forsyth,他帮助我提出了在 OrcsWeb 上宿主的 www.asp.net 服务器的这个问题。
本文就不讲述步骤了,请阅读 Brad Wilson 的文章,网址是 IIS6 Compression。还有一篇有关为 ASPX 启用压缩的知识库文章,网址是 Enable ASPX Compression in IIS。但是您应该注意,由于一些实施细节,IIS 6.0 中不能同时存在动态压缩和内核缓存。
技巧 10 — 服务器控件视图状态
视图状态是一个有趣的名称,用于表示在所生成页的隐藏输出字段中存储一些状态数据的ASP.NET。当该页张贴回服务器时,服务器可以分析、验证、并将此视图状态数据应用回该页的控件树。视图状态是一个非常强大的功能,因为它允许状态与客户端一起保持,并且它不需要 cookie 或服务器内存即可保存此状态。很多 ASP.NET 服务器控件都使用视图状态来保持在与页元素进行交互期间创建的设置,例如保存对数据进行分页时显示的当前页。
然而使用视图状态也有一些缺点。首先,服务或请求页时,它都会增加页的总负载。对张贴回服务器的视图状态数据进行序列化或取消序列化时,也会发生额外的开销。最后,视图状态会增加服务器上的内存分配。
几个服务器控件有着过度使用视图状态的趋势,即使在并不需要的情况下也要使用它,其中最著名的是 DataGrid。ViewState 属性的默认行为是启用,但是如果您不需要,则可以在控件或页级别关闭。在控件内,只需将 EnableViewState 属性设置为 false,或者在页中使用下列设置即可对其进行全局设置:
<%@ Page EnableViewState="false" %>
如果您不回发页,或者总是针对每个请求重新生成页上的控件,则应该在页级别禁用视图状态。
我为您讲述了一些我认为在编写高性能 ASP.NET 应用程序时有所帮助的技巧。正如我在本文前面部分提到的那样,这是一个初步指南,并不是 ASP.NET 性能的最后结果。(有关改善 ASP.NET 应用程序性能的信息,请参阅 Improving ASP.NET Performance。)只有通过自己的亲身体验才能找出解决具体性能问题的最好方法。但是,在您的旅程中,这些技巧应该会为您提供一些好的指南。在软件开发中,几乎没有绝对的东西;每个应用程序都是唯一的。
|
ASP.NET 2.0 中 Web 事件
ASP.NET 2.0 包含了内置的事件,包括心跳、应用程序生存期事件(启动/停止/编译)和错误陷阱事件(未处理异常)。不过,,您可以很容易地在这些基类之上进行构建,以从应用程序重创建并引发您自己的事件。举例来说,您可能创建一个自定义的事件来记录何时第一百个用户单击某一特定的链接。
ASP.NET 2.0 健康监视系统真正强大的功能是,通过 web.config 和 machine.config 文件它是完全可配置的。使用正常的 XML,您能定义事件、定义提供程序(事件接收器),以及将特定的事件发送到特定的提供程序。
创建事件 事件在结构上与异常是类似的。也就是说,除了作为一个消息容器,event 类本身几乎没有功能。在健康监视方面,所有事件都从 Sytstem.Web.Management.WebBaseEvent 继承。不过,您也可以从用于专用目的(如收集 HTTP 请求数据或处理异常)的高级基类派生。
列表 4. 自定义事件
using System; using System.Web.Management;
public class CustomEvent : WebBaseEvent { public const int EventCode = WebEventCodes.WebExtendedBase + 10; public MyEvent(string message, object eventSource) : base(message, eventSource, EventCode) { } }
创建自定义事件的最重要部分是提供一个唯一的 EventCode。所有内置事件代码都在 WebEventCodes 枚举中。自定义事件应该有从 WebEventCodes.WebExtendedBase + 1 开始的数字。除此之外创建自定义事件中的唯一常见任务是正确地初始化事件。
使用事件 虽然内置事件自动激发,但您还是可以将代码添加到应用程序以在任何时候启动自定义事件。
列表 5. 引发一个事件
<script runat="server"> void Page_Load(Object sender, EventArgs e) { // Raise a custom event MyEvent myEvent = new MyEvent("loading webevent sample page", this); myEvent.Raise(); } </script>
当您从 ASP.NET 页面引发一个事件时,您只是创建该事件的一个新实例然后执行 Raise() 方法。Raise() 方法将事件实例自动传递到健康监视引擎。然后此引擎将该事件映射到配置文件和提供程序,并将该事件移交给正确的提供程序。提供程序最终将事件传递到正确的接收器。
配置健康监视 健康监视是在 machine.Config 或 Web.Config 文件中的新的 区域配置的。您可以配置 部分来设置一个周期性报告应用程序状态的 Web 检测信号。您也可以配置应用程序来生成事件,并将该事件通过各种提供程序传递。
配置事件 必须在事件映射区域标识每个事件。事件通过唯一名称和完整类型被标识。事件名称在规则区域是作为链接来使用的。
列表 6. 事件映射区域
<!-- Event mappings define the events that are monitored --> <eventMappings> <add name="SampleWebRequests" type="Samples.ASPNet.SampleWebRequestEvent, SampleWebRequestEvent, Version=0.0.0.0, Culture=neutral, PublicKeyToken=f0c63b9a560d5e5a"/> </eventMappings>
ASP.NET 2.0 附带几个内置事件,配置为下列名称:
• 所有事件。所有事件名称捕获任何 WebBaseEvent。这个事件类别是对被健康监视系统捕获的每个事件的一个广泛的 catch-all。 • 检测信号。检测信号事件使用 WebHeartBeatEvent 提供关于 Web 应用程序状态的定期通知。 • 应用程序生存期事件。应用程序生存期事件包括启动和停止应用程序,以及重新编译应用程序的部分或全部。这些事件是基于 WebApplicationLifetimeEvent 的,并且在 lifetime 事件发生时,报告日期、时间和当前的状态。 • 所有错误。所有错误类别收集系统检测到的任何异常或错误。这些事件是基于 WebBaseErrorEvent 的。
• 基础结构错误。使用 WebErrorEvent 捕获与 ASP.NET 运行库或 IIS 相关的错误。这些事件是所有错误类别的子类,并且主要与系统管理员而不是应用程序开发者相关。
• 请求处理错误。在请求期间发生的任何错误或异常都会触发一个 WebRequestErrorEvent。这个事件记录了进入的请求以及与处理该请求相关联的错误。请求处理错误也是所有错误类别的一个子集。 • 所有审核。可使用健康监视系统通过 WebAuditEvent 提供审核尝试。这个事件自动记录 Web 应用程序中活动用户的操作。如果您正在使用模拟,审核事件将帮助您对谁在使用您的应用程序,以及他们是如何在使用保持跟踪。 • 失败审核。WebFailureAuditEvent 是一个特殊的审核事件类型,当一个用户试图使用无效的用户名或密码登录您的 Web 站点时,它就会被触发。当一个对于指定资源用户是无法验证的时候,此事件也会发生。 • 成功审核。WebSuccessAuditEvent 是失败事件的对应,只要用户已验证或执行一些其他需要审核记录的操作就会发生。
自定义事件是易于创建的,尽管您必须向您的应用程序添加代码来引发事件。
配置提供程序 每个提供程序都必须在配置文件中注册。注册一个提供程序需要应用程序的唯一名称和类型。这个类型包含了实际 provider 类的完整强名称 (strong name)。提供程序的名称被用作规则区域中的一个链接。
列表 7. 提供程序
<healthMonitoring Enabled="true" heartBeatInterval="0"> <!-- Providers link health events to various targets such as WMI or SMTP email --> <providers> <add name="WmiEventProvider" type="System.Web.Management.WebWmiEventProvider, System.Web,Version=1.2.3400.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/> </providers>
ASP.NET 2.0 附带有 WMI、Windows 事件监视器、SMTP 电子邮件和 SQL Server 数据库的提供程序。通过扩展正确的基类,可以创建自定义提供程序来连接到其他事件接收器。
将事件映射到提供程序
配置健康监视的最后一步是,用规则将事件连接到提供程序。规则提供了在好的事件名称、事件类、提供程序和事件配置文件或者类别之间的一个链接。您也可以使用规则来定义启动特定的事件的一个最短时间间隔。
列表 8. 规则
<!-- Rules link events to providers and profiles, and define intervals for event checking --> <rules> <add name="Custom Database Events" eventName="CustomDBEvents" provider="WmiEventProvider" profile="Database" minInterval="00:01:00" /> <add name="Standard Web Requests" eventName="All Events" provider="SqlEventProvider" profile="Default" minInterval="00:01:00" /> </rules> </healthMonitoring>
规则执行由不同属性配置的若干不同任务:
• name。规则名称是友好名称,它会在事件被发送到接收器时出现。 • eventName。eventName 映射到一个在 区域中配置的事件。 • provider。提供程序是一个到在 区域中配置的提供程序的链接。任何匹配这个规则的事件将通过这个提供程序传递给由该提供程序支持的目标。举例来说,System.Web.Management.SqlWebEventProvider 会自动将事件写入到 SQL Server 数据库。 • profile。不同的提供程序将配置文件属性作为显示事件的过滤器来使用。举例来说,电子邮件提供程序可能立即为任何带有“紧急”配置文件的事件发送一个消息,但是也可能只发送带有该日“例行”配置文件事件的汇编的日常电子邮件。 • minInterval。一些事件,如检测信号,必须在最短的时间间隔内激发。您可以使用这个属性来设置最小事件时间间隔。
通过这个配置区域,您可以设置各种健康相关事件,并将事件映射到各种提供程序。举例来说,您可能设置一个检测信号,每 10 分钟发送一个 WMI 事件。同样,您可以为任何未捕获的异常设置一个电子邮件警报。
|
[asp.net]扩展Forms验证
1.使用Forms验证存储用户自定义信息
Forms验证在内部的机制为把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,因为是经过特殊加密的,所以应该来说是比较安全的。而.net除了用这个票据存放自己的信息外,还留了一个地给用户自由支配,这就是现在要说的UserData。
UserData可以用来存储string类型的信息,并且也享受Forms验证提供的加密保护,当我们需要这些信息时,也可以通过简单的get方法得到,兼顾了安全性和易用性,用来保存一些必须的敏感信息还是很有用的。
下面来看怎么使用UserData,然后会给出一个实际使用的例子。
//创建一个新的票据,将客户ip记入ticket的userdata FormsAuthenticationTicket ticket=new FormsAuthenticationTicket( 1,userName.Text,DateTime.Now,DateTime.Now.AddMinutes(30), false,Request.UserHostAddress); //将票据加密 string authTicket=FormsAuthentication.Encrypt(ticket); //将加密后的票据保存为cookie HttpCookie coo=new HttpCookie(FormsAuthentication.FormsCookieName,authTicket); //使用加入了userdata的新cookie Response.Cookies.Add(coo);
下面是FormsAuthenticationTicket构造函数的重载之一的方法签名 public FormsAuthenticationTicket( int version, string name, DateTime issueDate, DateTime expiration, bool isPersistent, string userData );
参数 version 版本号。 name 与身份验证票关联的用户名。 issueDate Cookie 的发出时间。 expiration Cookie 的到期日期。 isPersistent 如果 Cookie 是持久的,为 true;否则为 false。 userData 将存储在 Cookie 中的用户定义数据
使用userdata也很简单,FormsIdentity的Ticket属性就提供了对当前票据的访问,获得票据后就可以用UserData属性访问保存的信息,当然是经过解密的。 ((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData
下面是一个具体的应用。
由于Forms验证是通过cookie来进行的,它需要传递一个票据来进行工作。虽然票据是加密的,里面的内容不可见,但这并不能阻止别人用一个假冒的身份使用票据(就像我们可以拿别人的钥匙去开别人的锁),比较常见的就是不同ip的用户在不安全通道截获了这个票据,然后使用它进行一些安全范围外的活动。
解决这个问题的办法之一就是使用SSL来传递信息。
但是如果不能使用SSL呢?我们可以判断ip和票据是否匹配,如果发出请求的ip是初次产生票据的ip,则没有问题,否则就销毁这个票据。
为此,我们需要在一开始处理登录时将用户的ip保存起来,这样就可以在以后的请求中随时验证后继请求的ip是否和初始ip相同。保存这个敏感ip的最佳场所当然是UserData啦,而验证的时机则是在AuthenticateRequest事件发生时,即Global.aspx.cs中定义的处理此事件的Application_AuthenticateRequest方法中。
上面的示例实际上已经是把用户ip保存到了UserData中,下面是验证的过程。
if(this.Request.IsAuthenticated) { if(((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData !=this.Request.UserHostAddress) { System.Security.Principal.GenericIdentity gi=new System.Security.Principal.GenericIdentity("",""); string[] rolesi={}; System.Security.Principal.GenericPrincipal gpi=new System.Security.Principal.GenericPrincipal(gi,rolesi); this.Context.User=gpi; } }
通过给GenericPrincipal空的GenericIdentity和roles使票据失效,这样将强迫用户重新登录。为了测试这个方法,可以先把条件改为相等,看效果如何 :)
这个方法也有不足之处,具体为:
1.使用同一代理的用户将拥有同一个ip,这样就不能防范此类假冒攻击了
2.如果用户使用动态ip,则可能造成正常用户被我们强行销毁票据。不过总的来说,这个办法还是比较可行的。
|
正则表达式
Email : /^/w+([-+.]/w+)*@/w+([-.]/w+)*/./w+([-.]/w+)*$/,
Phone : /^((/(/d{3}/))|(/d{3}/-))?(/(0/d{2,3}/)|0/d{2,3}-)?[1-9]/d{6,7}(/-/d{1,4})?$/,
Mobile : /^((/(/d{3}/))|(/d{3}/-))?13/d{9}$/,
Url : /^http:[A-Za-z0-9]+/.[A-Za-z0-9]+[//=/?%/-&_~`@[/]/':+!]*([^<>/"/"])*$/,
IdCard : /^/d{15}(/d{2}[A-Za-z0-9])?$/,
Currency : /^/d+(/./d+)?$/,
Number : /^/d+$/,
Zip : /^[1-9]/d{5}$/,
QQ : /^[1-9]/d{4,8}$/,
Integer : /^[-/+]?/d+$/,
Double : /^[-/+]?/d+(/./d+)?$/,
English : /^[A-Za-z]+$/,
Chinese : /^[/u0391-/uFFE5]+$/,
Username : /^[a-z]/w{3,}$/i,
UnSafe : /^(([A-Z]*|[a-z]*|/d*|[-_/~!@#/$%/^&/*/./(/)/[/]/{/}<>/?/'/"]*)|.{0,5})$|/s/
日期:^/d{4}/-/d{1,2}/-/d{1,2}