第十三节:HttpHander扩展及应用(自定义扩展名、图片防盗链)
一. 自定义扩展名
1. 前言
凡是实现了IHttpHandler接口的类均为Handler类,HttpHandler是一个HTTP请求的真正处理中心,在HttpHandler容器中,ASP.NET Framework才调用HttpHandler的ProcessRequest方法来对这个HTTP请求进行真正的处理,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。
2. 背景
我们在日常开发中,也许会碰到一些这样的特殊需求,在路由规则之外,想给自己预留一个后门,进行一些小动作;或者想自定义一种特有的看起来很酷炫的后缀;或者默写后缀不想走MVC框架默认提供的处理流程,那么使用HttpHander进行扩展,再合适不过。
下面我们先扩展一个后缀为 .ypf的处理请求,凡是该后缀的请求,统一显示验证码页面。
3. 详细步骤
①. 新建一个类(ypfCode),实现IHttpHandler接口。
1 /// <summary> 2 ///这里自定义的handle为一般处理程序,实现了IHttpHandler接口 3 ///IRequiresSessionState是为了使http请求具有会话状态的读写权限,即能操控: context.Session["CheckCode"] = code; 4 /// </summary> 5 public class ypfCode : IHttpHandler, IRequiresSessionState 6 { 7 /// <summary> 8 /// 您将需要在网站的 Web.config 文件中配置此处理程序 9 /// 并向 IIS 注册它,然后才能使用它。有关详细信息, 10 /// 请参阅以下链接: https://go.microsoft.com/?linkid=8101007 11 /// </summary> 12 #region IHttpHandler Members 13 14 public bool IsReusable 15 { 16 // 如果无法为其他请求重用托管处理程序,则返回 false。 17 // 如果按请求保留某些状态信息,则通常这将为 false。 18 get { return true; } 19 } 20 21 public void ProcessRequest(HttpContext context) 22 { 23 //在此处写入您的处理程序实现。 24 string code = ""; 25 Bitmap bitmap = VerifyCodeHelper.CreateVerifyCode(out code); 26 context.Session["CheckCode"] = code; 27 bitmap.Save(context.Response.OutputStream, ImageFormat.Gif); 28 context.Response.ContentType = "image/gif"; 29 } 30 #endregion 31 }
补充一个验证码实现类:
1 public class VerifyCodeHelper 2 { 3 public static Bitmap CreateVerifyCode(out string code) 4 { 5 //建立Bitmap对象,绘图 6 Bitmap bitmap = new Bitmap(200, 60); 7 Graphics graph = Graphics.FromImage(bitmap); 8 graph.FillRectangle(new SolidBrush(Color.White), 0, 0, 200, 60); 9 Font font = new Font(FontFamily.GenericSerif, 48, FontStyle.Bold, GraphicsUnit.Pixel); 10 Random r = new Random(); 11 string letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ0123456789"; 12 13 StringBuilder sb = new StringBuilder(); 14 15 //添加随机的五个字母 16 for (int x = 0; x < 5; x++) 17 { 18 string letter = letters.Substring(r.Next(0, letters.Length - 1), 1); 19 sb.Append(letter); 20 graph.DrawString(letter, font, new SolidBrush(Color.Black), x * 38, r.Next(0, 15)); 21 } 22 code = sb.ToString(); 23 24 //混淆背景 25 Pen linePen = new Pen(new SolidBrush(Color.Black), 2); 26 for (int x = 0; x < 6; x++) 27 graph.DrawLine(linePen, new Point(r.Next(0, 199), r.Next(0, 59)), new Point(r.Next(0, 199), r.Next(0, 59))); 28 return bitmap; 29 } 30 }
②. 在Web.config文件中的 <system.webServer>→<handlers>下添加以下节点,表示以.ypf为后缀的请求统一由ypfCode处理,其中
<add name="ypf" path="*.ypf" verb="*" type="Ypf.Web.Core.PipeLine.ypfCode,Ypf.Web.Core" />
Ypf.Web.Core.PipeLine.ypfCode:表示该类的命名空间
Ypf.Web.Core:表示该类所在库的程序集名称
1 <!--VS2013及以后/IIS7.0之后的集成模式 需要添加下面的system.webServer节点--> 2 <system.webServer> 3 4 <!--1. 在此处配置modules--> 5 <modules runAllManagedModulesForAllRequests="false"> 6 <!--1.1 runAllManagedModulesForAllRequests处理静态文件的请求--> 7 <remove name="FormsAuthentication" /> 8 <!--1.2 优化网站性能,去掉不需要的module--> 9 <remove name="RoleManager" /> 10 <remove name="FileAuthorization" /> 11 <remove name="UrlAuthorization" /> 12 </modules> 13 <!--2. 在此处配置handlers--> 14 <handlers> 15 <!--2.1 优化网站性能,去掉不需要的module--> 16 <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> 17 <remove name="OPTIONSVerbHandler" /> 18 <remove name="TRACEVerbHandler" /> 19 <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> 20 21 <!--2.2 添加自定义handlers--> 22 <!--2.2.1 后缀为ypf的请求 --> 23 <add name="ypf" path="*.ypf" verb="*" type="Ypf.Web.Core.PipeLine.ypfCode,Ypf.Web.Core" /> 24 25 <!--2.2.2 图片处理请求 --> 26 <!--方案一 逐个添加不同格式的图片 特别注意:不支持在一个path写多个扩展名--> 27 <!--<add name="img" path="*.jpg" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" /> 28 <add name="png" path="*.png" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" /> 29 <add name="gif" path="*.gif" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" />--> 30 <!--方案二 通过HangerFactory来处理 特别注意:不支持在一个path写多个扩展名 --> 31 <add name="img" path="*.jpg" verb="*" type="Ypf.Web.Core.PipeLine.ImageHandlerFactory,Ypf.Web.Core" /> 32 <add name="png" path="*.png" verb="*" type="Ypf.Web.Core.PipeLine.ImageHandlerFactory,Ypf.Web.Core" /> 33 <add name="gif" path="*.gif" verb="*" type="Ypf.Web.Core.PipeLine.ImageHandlerFactory,Ypf.Web.Core" /> 34 35 </handlers> 36 </system.webServer>
③. 由于本身框架本身已经有一套路由规则用来处理http请求,所以需要在RouteConfig中忽略该自定义Handle的规则,如下:
routes.IgnoreRoute("MyTest/{*pathInfo}"); 表示MyTest/格式的请求不进行路由验证
④. 测试:http://localhost:7559/MyTest/xxx.ypf 均返回验证码图片
二. 图片防盗链
1. 什么是图片防盗链
自己网站中展示的自己的图片通常是放在自己服务器上,很多无耻的网站获取到别人网站的图片地址,放到自己网站上,这就是图片盗链。
图片被盗链损耗的是自己服务器的流量。
2. 防盗链的代码实现原理
当接受到一张图片请求的时候
1. 判断该图片请求是从哪一个请求连接过来的(或者从哪一个页面连接过来的),如果为空,表示是一个单独的图片请求,肯定不该站内的,
所以判断为盗链。两种判断方式如下
a: context.Request.ServerVariables["HTTP_REFERER"]
b: context.Request.UrlReferrer
2. 如果上一个请求不为空,则判断上一个请求中是否含有该站的域名或ip地址,如果没有,则为非法请求,不是该站内的请求,
所以判断为盗链。两种判断方法
a:context.Request.UrlReferrer.Host.Contains("121.42.223.23")
b:context.Request.ServerVariables["HTTP_REFERER"].Contains("121.42.223.23")
3. 自定义hander进行处理
①. 自定义imgHandle,实现IHttpHandler接口
1 /// <summary> 2 /// 图片防盗链 3 /// 该自定义handle用来处理各种获取图片的请求 4 /// (这里处理 jpg gif png三种格式的图片) 5 /// </summary> 6 public class imgHandle : IHttpHandler 7 { 8 9 public bool IsReusable 10 { 11 // 如果无法为其他请求重用托管处理程序,则返回 false。 12 // 如果按请求保留某些状态信息,则通常这将为 false。 13 get { return true; } 14 } 15 16 public void ProcessRequest(HttpContext context) 17 { 18 19 //0.获取图片的返回类型 20 string type = GetContentType(context.Request.Url.ToString()); 21 22 var beforeUrl = context.Request.ServerVariables["HTTP_REFERER"]; 23 24 //1. 如果请求的url为空,或者请求的主机部分为空,返回一张禁止倒链的图片 25 if (context.Request.UrlReferrer == null || context.Request.UrlReferrer.Host == null) 26 { 27 //context.Response.ContentType = "image/JPEG"; 28 //context.Response.WriteFile("/Content/imageHandle/Forbidden.jpg"); 29 30 context.Response.Write("您的请求是非法,请勿再试"); 31 } 32 else 33 { 34 //2. url不空,且包含自己主机域名,表示为自己网站的请求,显示正常图片 35 //正常发布的时候这里用域名或者ip,localhost为了本地调试 36 if (context.Request.UrlReferrer.Host.Contains("121.42.200.127")) 37 { 38 string FileName = context.Server.MapPath(context.Request.FilePath); 39 context.Response.ContentType = type; 40 context.Response.WriteFile(FileName); 41 } 42 else 43 { 44 //3. url不空,但不包含自己主机域名,表示该请求为盗链请求,返回一张禁止倒链的图片 45 //context.Response.ContentType = "image/JPEG"; 46 //context.Response.WriteFile("/Content/imageHandle/Forbidden.jpg"); 47 48 context.Response.Write("您的请求是非法,请勿再试"); 49 } 50 51 } 52 } 53 54 /// <summary> 55 /// 将地址转换成图片返回值 56 /// </summary> 57 /// <param name="url"></param> 58 /// <returns></returns> 59 private static string GetContentType(string url) 60 { 61 switch (Path.GetExtension(url)) 62 { 63 case ".gif": 64 return "Image/gif"; 65 case ".jpg": 66 return "Image/jpeg"; 67 case ".png": 68 return "Image/png"; 69 default: 70 break; 71 } 72 return null; 73 } 74 75 }
扩展:也可以自定义handleFactory,实现IHttpHandlerFactory接口,在handleFactory中指定不同hander。
1 /// <summary> 2 /// 自定义的一个HandleFactory,用来指定不同的后缀调用不同的Handle 3 /// 这里是一个ImageHandlerFactory,处理不同 图片后缀 4 /// </summary> 5 public class ImageHandlerFactory : IHttpHandlerFactory 6 { 7 public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) 8 { 9 string path = context.Request.PhysicalPath; 10 if (Path.GetExtension(path).Equals(".jpg")) 11 { 12 return new imgHandle(); 13 } 14 else if (Path.GetExtension(path).Equals(".png")) 15 { 16 return new imgHandle(); 17 } 18 else 19 { 20 //这里可以继续扩展,这里测试不继续扩展了 21 return new imgHandle(); 22 } 23 24 } 25 26 public void ReleaseHandler(IHttpHandler handler) 27 { 28 29 } 30 }
②. 在Web.config文件中的 <system.webServer>→<handlers>下添加:
<add name="img" path="*.jpg" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" />
<add name="png" path="*.png" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" />
<add name="gif" path="*.gif" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" />
1 <!--VS2013及以后/IIS7.0之后的集成模式 需要添加下面的system.webServer节点--> 2 <system.webServer> 3 4 <!--1. 在此处配置modules--> 5 <modules runAllManagedModulesForAllRequests="false"> 6 <!--1.1 runAllManagedModulesForAllRequests处理静态文件的请求--> 7 <remove name="FormsAuthentication" /> 8 <!--1.2 优化网站性能,去掉不需要的module--> 9 <remove name="RoleManager" /> 10 <remove name="FileAuthorization" /> 11 <remove name="UrlAuthorization" /> 12 </modules> 13 <!--2. 在此处配置handlers--> 14 <handlers> 15 <!--2.1 优化网站性能,去掉不需要的module--> 16 <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> 17 <remove name="OPTIONSVerbHandler" /> 18 <remove name="TRACEVerbHandler" /> 19 <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> 20 21 <!--2.2 添加自定义handlers--> 22 <!--2.2.1 后缀为ypf的请求 --> 23 <add name="ypf" path="*.ypf" verb="*" type="Ypf.Web.Core.PipeLine.ypfCode,Ypf.Web.Core" /> 24 25 <!--2.2.2 图片处理请求 --> 26 <!--方案一 逐个添加不同格式的图片 特别注意:不支持在一个path写多个扩展名--> 27 <add name="img" path="*.jpg" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" /> 28 <add name="png" path="*.png" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" /> 29 <add name="gif" path="*.gif" verb="*" type="Ypf.Web.Core.PipeLine.imgHandle,Ypf.Web.Core" /> 30 <!--方案二 通过HangerFactory来处理 特别注意:不支持在一个path写多个扩展名 --> 31 <!--<add name="img" path="*.jpg" verb="*" type="Ypf.Web.Core.PipeLine.ImageHandlerFactory,Ypf.Web.Core" /> 32 <add name="png" path="*.png" verb="*" type="Ypf.Web.Core.PipeLine.ImageHandlerFactory,Ypf.Web.Core" /> 33 <add name="gif" path="*.gif" verb="*" type="Ypf.Web.Core.PipeLine.ImageHandlerFactory,Ypf.Web.Core" />--> 34 35 </handlers> 36 </system.webServer>
4. 代码测试
新建一个Index页面,在该页面通过img标签直接链接到图片地址,显示图片,然后将该项目发布到测试服务器上。
①:访问地址:http://121.42.200.127:8099/Pipe/Index, 正常显示该页面中的图片, 表示该图片请求为站内正常的请求
②:访问地址:http://121.42.200.127:8099/Content/imageHandle/pipe1.jpg, 不能正常访问,表示该图片请求为盗链