| ||||
ASP.NET运行机制 1 Http请求
如上图,我们先来真正了解一下什么是http请求,举一个简单的例子,你在浏览的一张网页上点击其中的一个超级链接”Microsoft程序员测试题”,如果这个链接到的地址是 http://www.frontfree.net/view/article_840.html ,那么浏览器会立刻向www.frontfree.net 这个服务器发送一条http请求.但是这个请求中不仅包括 http://www.frontfree.net/view/article_840.html字符串,而且还包括 这个www.frontfree.net/article字符串,这个信息是你的超级链接的参考页地址,或参考页信息,这个信息其实很重要,这个可以用在我们后文中所述的对图片盗链的判断中。当然这也是在http2.0规范中被要求的,其次还有一些其它信息,比如你请求的主机名,经过的路由器,客户端的一些信息等等,所以一个http请求中包括相当多的信息,并不像我们从表面上想到的那样简单.好了,现在我们已经知道了什么是http请求.接下来就是时候讲到ASP.NET的网络应用程序的运行机制了。 2 ASP.NET运行原理概述
如上图,当一个http请求发送过来并被IIS机收到之后,IIS首先通过你请求的页面类型为其加载相应的dll文件,然后在处理过程中将这条请求发送给能够处理这条请求的模块,而在ASP.NET中这个模块就叫做HttpHandler,为什么aspx这样的文件可以被服务器处理,那是因为在服务器端有默认的HttpHandler专门处理aspx文件,IIS再将这条请求发送给能够处理这条请求的模块之前,还需要经过一些HttpModule,这些都是系统默认的Modules,而且在这个http请求传到HttpHandler之前要经过不同的HttpModuls的处理,这就像我们如果要乘坐国际航班飞抵异国他乡的话,在你真正坐上飞机前,要经过购买机场建设费,办理登记手续核实身份,进行行李打包托运,进行安全检察,现在又要进行体温等检察一样,需要经一系列的严格的手续。这样做有什么好处,一是为一些必须的过程,二是为了安全性,三是为了提高效率,四是为了我们能够在更多的环节上进行控制,即增强了我们的控制能力。而上述的都是系统的,怎么能说是我们的对http请求控制能力能,那么我们是否可以建立自己的HttpModule和HttpHandler吗?答案是肯定的。但我们先不要急于做事,让我们再深入研究一下运行机制吧。 3 ASP.NET运行机制
那么上面这张图让我们能够更清楚地看到一个http请求是如何经过服务器的处理的,同时通过这张图我们也可以看出Request是掌管着所有客户端输入的。图中为我们展示了一个http请求有可能经过的四条路线。当你第一次访问这个页面时这个请求首先依次经过HttpModuls和HttpHandler的处理,而在HttpHandler的处理中服务器会为你转到你真正要访问的页面,然后通过ASP Engine来找到这个页面背后的类,并实例化为一个临时对象,再此过程中会触发一系列的事件,其中一部份的事件需要经过对象中的方法处理,之后服务器会将这个处理后页面发移交给Response对象,然后由Response对象将这个页面发送到客户端。这就是第一条路线,而当你在这个页面上进行重新提交一些信息,并继续向服务器发送请求时,因为你的服务器之间的会话已经建立,对于你所在的那个临时对象在服务器中已经建立,所以不用再经过初始化页面的工作,故这第二条路线是按照 HttpModuls,HttpHandler,然后直接与临时对象交互,然后返回的。第三条路线与第二条不同的是在处理请求时如果涉及到需要调用ASP Cache,既ASP 缓存的,而临时对象回将直接从ASP 缓存提取信息并返回.这第四条路线就是当你刷新这个页面的时候,服务器接收到时发现这个请求先前已经处理过了,并将处理结果存储到由一个默认的HttpModule管理的输出缓存中了,那么我们就可以直接从这个缓存提取信息并返回,而无需再重新处理一遍了。 图片盗链问题 1什么是图片盗链 让我们先分析一般的浏览现象,其中最重要的一点就是一个完整的页面并不是一次全部传送到客户端的.如果你请求的是一个带有许多图片和其它信息的页面,那么最先的一个Http请求被传送回来的是这个页面的文本,然后通过客户端的浏览器对这段文本的解释执行,发现其中还有图片,那么客户端的浏览器会再发送一条Http请求,当这个请求被处理后那么这个图片文件会被传送到客户端,然后浏览器回将图片安放到页面的正确位置,就这样一个完整的页面也许要经过发送多条Http请求才能够被完整的显示.基于这样的机制,就会产生一个问题,那就是盗链问题:就是一个网站中如果没有起页面中所说的信息,例如图片信息,那么它完全可以将这个图片的连接到别的网站.这样没有任何资源的网站利用了别的网站的资源来展示给浏览者,提高了自己的访问量,而大部分浏览者又不会很容易地发现,这样显然,对于那个被利用了资源的网站是不公平的. 2 解决方法 那么现在利用ASP.NET中的这HttpHandler能够很好地解决这个问题,之所以能够发生这个问题.就是因为我们在默认状态下只处理那些动态的网页,象asp,aspx等等,但当有请求一个图片文件时,IIS就会直接提取资源并发送给客户端,这样看来就显得有些盲目了吧 所以我们要创建自己的HttpHandler来处理图片文件。例如jpg文件。 3 那么我们如何建立自己的HttpHandler并在web应用程序中注册呢? (1)建立自己的HttpHandler 创建一个继承了System.Web.IHttpHandler接口的类,在System.Web.IHttpHandler接口只有两个成员 1 IsReusable 属性,其返回一个值代表其他http请求是否可以使用当前继承了 System.Web.IHttpHander接口的类的实例。 2 ProcessRequest(System.Web.HttpContext context) 方法,除了用户自定义中被要求处理的特殊的http请求。 其中的参数 System.Web.HttpContext 类的实例装入了一个http请求中http协议中要求的所有信息。其中System.Web.HttpContext 类中包含有属性 Request 使得从客户端发送过来的http请求信息的值可以被方便地读取;属性Response ,其中封装了需要返回给客户端的信息和操作。当然还有许多常用的属性和方法,在此就不详述了,我们这里只用到这两个属性。 (2)web应用程序中注册自定义的HttpHandler 在 Web.config这个网络应用程序配置文件中加入注册信息
4 那么我们看看在ProcessRequest(System.Web.HttpContext context)方法是如何对请求图片文件的http请求进行处理的。
5 待这一切都做好了,我们还要在让IIS知道,遇到图片文件也要进行处理。
这件事情我们还要让IIS知道.而为什么aspx文件会被处理,而默认的那些可以处理aspx的HttpHandler在哪里,其实是因为IIS在接收到aspx页面请求时,会为其自动加载一个C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/aspnet_isapi.dll文件,这个文件是专为处理aspx页面所用的,是.NET Framework的一部份,我们要IIS在接收到jpg文件请求时也为其加载这个dll文件。 我们首先添加一个处理,读者可以参考aspx文件的配置就可以对处理jpg文件进行配置,我们同样也为jpg文件加载aspnet_isapi.dll文件,因为我们对jpg文件的处理也是建立在 ASP.NET机制之上的。具体配置,如下图:
OK,现在你已经对此方法有一定的了解了,其实自定义HttpHandler还有其他的应用,在此我们就不赘述了,下面的工作就需要读者多多实践了,并从中学习到更多的东西获得更多的经验,更好地应用设计开发的需求。 |
==================================================
ASP.NET 防盗链源码
/* * * 防盗链IHttpHandler * * * 增加了对文件关键字的选择(即仅对文件名存在某些关键字或不存在某些关键字进行过滤) * 设置web.config中<appSettings>节以下值 * string eWebapp_NoLink 如果文件名符合该正确表态式将进行过滤(不设置对所有进行过滤) * string eWebapp_AllowLink 如果文件名符合该正确表态式将不进行过滤(优先权高于AllowLink,不设置则服从AllowLink) * bool eWebapp_ AllowOnlyFile 如果为False,(默认true)则不允许用户直接对该文件进行访问建议为true * * * :)以下设置均可省略,设置只是为了增加灵活性与体验 * eWebapp_NoLink_Message 错误信息提示:默认为Link From:域名 * eWebapp_Error_Width 错误信息提示图片宽 * eWebapp_Error_Height 错误信息提示图片高 * * * * 垃圾猪 2005-9-11 创建 * http://ewebapp.net */ using System; using System.Web; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Configuration; using System.Text.RegularExpressions; namespace eWebapp { /// <summary> /// 防盗链IHttpHandler /// 参考http://www.softat.org/archiver/tid-52114.html /// 垃圾猪 2005-9-12 修正 /// </summary> public class NoLink : IHttpHandler { private string eWebapp_NoLink = string.Empty; private string eWebapp_AllowLink = string.Empty; private bool eWebapp_AllowOnlyFile = true; private string eWebapp_NoLink_Message = string.Empty; private bool error = false; public NoLink() { // // TODO: 在此处添加构造函数逻辑 // } public void ProcessRequest(HttpContext context) { eWebapp_NoLink_Message = ConfigurationSettings.AppSettings["eWebapp_NoLink_Message"]; string myDomain = string.Empty; error = errorLink(context, out myDomain); if (Empty(eWebapp_NoLink_Message)) { eWebapp_NoLink_Message = "Link from :" + myDomain; } if (error) { //Jpg(context.Response,eWebapp_NoLink_Message); Jpg(context.Response, eWebapp_NoLink_Message); } else { Real(context.Response, context.Request); } } public bool IsReusable { get { return true; } } /// <summary> /// 输出错误信息 /// </summary> /// <param name="Response"></param> /// <param name="_word"></param> private void Jpg(HttpResponse Response, string _word) { int myErrorWidth = _word.Length * 15; int myErrorHeight = 16; try { int _myErrorWidth = Convert.ToInt32(ConfigurationSettings.AppSettings["eWebapp_Error_Width"]); if (_myErrorWidth > 0) { myErrorWidth = _myErrorWidth; } } catch { } try { int _myErrorHeight = Convert.ToInt32(ConfigurationSettings.AppSettings["eWebapp_Error_Height"]); if (_myErrorHeight > 0) { myErrorHeight = _myErrorHeight; } } catch { } Bitmap Img = null; Graphics g = null; MemoryStream ms = null; Img = new Bitmap(myErrorWidth, myErrorHeight); g = Graphics.FromImage(Img); g.Clear(Color.White); Font f = new Font("Arial", 9); SolidBrush s = new SolidBrush(Color.Red); g.DrawString(_word, f, s, 3, 3); ms = new MemoryStream(); Img.Save(ms, ImageFormat.Jpeg); Response.ClearContent(); Response.ContentType = "image/Gif"; Response.BinaryWrite(ms.ToArray()); g.Dispose(); Img.Dispose(); Response.End(); } /// <summary> /// 输出真实文件 /// </summary> /// <param name="response"></param> /// <param name="context"></param> private void Real(HttpResponse response, HttpRequest request) { FileInfo file = new System.IO.FileInfo(request.PhysicalPath); response.Clear(); response.AddHeader("Content-Disposition", "filename=" + file.Name); response.AddHeader("Content-Length", file.Length.ToString()); string fileExtension = file.Extension.ToLower(); //这里选择输出的文件格式 //可以参考http://ewebapp.cnblogs.com/articles/234756.html增加对更多文件格式的支持. switch (fileExtension) { case "mp3": response.ContentType = "audio/mpeg3"; break; case "mpeg": response.ContentType = "video/mpeg"; break; case "jpg": response.ContentType = "image/jpeg"; break; case "bmp": response.ContentType = "image/bmp"; break; case "gif": response.ContentType = "image/gif"; break; case "doc": response.ContentType = "application/msword"; break; case "css": response.ContentType = "text/css"; break; default: response.ContentType = "application/octet-stream"; break; } response.WriteFile(file.FullName); response.End(); } /// <summary> /// 确认字符串是否为空 /// </summary> /// <param name="_value"></param> /// <returns></returns> private bool Empty(string _value) { if (_value == null | _value == string.Empty | _value == "") { return true; } else { return false; } } /// <summary> /// 检查是否是非法链接 /// </summary> /// <param name="context"></param> /// <param name="_myDomain"></param> /// <returns></returns> private bool errorLink(HttpContext context, out string _myDomain) { HttpResponse response = context.Response; string myDomain = context.Request.ServerVariables["SERVER_NAME"]; _myDomain = myDomain; string myDomainIp = context.Request.UserHostAddress; eWebapp_NoLink = ConfigurationSettings.AppSettings["eWebapp_NoLink"]; eWebapp_AllowLink = ConfigurationSettings.AppSettings["eWebapp_AllowLink"]; try { eWebapp_AllowOnlyFile = Convert.ToBoolean(ConfigurationSettings.AppSettings["eWebapp_AllowOnlyFile"]); } catch { eWebapp_AllowOnlyFile = true; } if (context.Request.UrlReferrer != null) { //判定referDomain是否存在网站的IP或域名 string referDomain = context.Request.UrlReferrer.AbsoluteUri.Replace(context.Request.UrlReferrer.AbsolutePath, ""); string myPath = context.Request.RawUrl; if (referDomain.IndexOf(myDomainIp) >= 0 | referDomain.IndexOf(myDomain) >= 0) { return false; } else { //这里使用正则表达对规则进行匹配 try { Regex myRegex; //检查允许匹配 if (!Empty(eWebapp_AllowLink)) { myRegex = new Regex(eWebapp_AllowLink); if (myRegex.IsMatch(myPath)) { return false; } } //检查禁止匹配 if (!Empty(eWebapp_NoLink)) { myRegex = new Regex(eWebapp_NoLink); if (myRegex.IsMatch(myPath)) { return true; } else { return false; } } return true; } catch { //如果匹配出错,链接错误 return true; } } } else { //是否允许直接访问文件 if (eWebapp_AllowOnlyFile) { return false; } else { return true; } } } } } |