目錄
5. 通過 MVC 自定義 RouteHandler 防盜鏈
自己網站上的圖片被別的網站盜用是一件很令人厭惡的事情,下面是處理圖片盜鏈的幾種方法。
在這里先交代一下環境,我用的是 MVC4 ,IIS7 應用程序池為集成模式,以下配置都是基於此環境進行。
這是一個比較簡單,方便的方法。首先要去 Url Rewite 官網 下載 URL Rewrite Module 2.0 並安裝。安裝完成后可以看到 IIS設置里多了 URL重寫 的模塊如下圖:

在這里,可以對URL訪問規則進行設置, 雙擊 URL 重寫,添加入站規則


在條件(c) 里面添加 {HTTP_REFERER} 模式為: ^http://localhost/.*$, 意思是 請求 HTTP_REFERER 必須包含 http://localhost/ 字符,規則當然是根據自己的情況寫。
添加保存后,站點的 web.config 文件的 system.webServer 節點下就多了 rewrite 節點,配置如下。
<system.webServer> <validation validateIntegratedModeConfiguration="false" /> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> <rewrite> <rules> <rule name="防盜鏈" stopProcessing="true"> <match url=".*\.(gif|jpg|png)" /> <conditions> <add input="{HTTP_REFERER}" pattern="^http://localhost/.*$" negate="true" /> </conditions> <action type="Redirect" url="http://www.baidu.com" /> </rule> </rules> </rewrite> </system.webServer>
配置好了,有沒有效果呢,我們做一個測試頁面試試
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <img src="Content/webpage/img/111.jpg" /> <img src="111.jpg"/> </body> </html>
里面有2張圖片,由於在IIS “入站規則條件” 里面配置的 HTTP_REFERER 正則表達式為 ^http://localhost/.*$ 如果規則有效,我們訪問 http://localhost/HtmlPage1.html 圖片應正常顯示,而如果通過 http://127.0.0.1/HtmlPage1.html 訪問是不應該顯示圖片的,下面是我通過這兩個地址訪問效果。

說明配置是成功的。當然了,URL Rewrite Module 並非僅僅做圖片防盜鏈喲!
防盜鏈的原理都是一樣的,主要是通過 referer 判斷來源站點,如果來源站點不在 “白名單” 里,則拒絕或返回一張默認圖片
location ~.*\.(jpg|gif|png)$ { valid_referers none blocked *.abc.com abc.com; if ($invalid_referer) { #rewrite ^/ http://abc.com/error.html; return 403; } }
location ~.*\.(jpg|gif|png)$ 表示所有 以 jpg|gif|png 為后綴名的文件都進行防盜鏈處理
valid_referers none blocked *.abc.com abc.com; 驗證 referer 其中 none 表示直接訪問的,不存在referer blocked為根據防火牆偽裝的 referer
#rewrite ^/ http://abc.com/error.html; 如果圖片是放盜鏈,重定向到 地址 http://abc.com/error.html,一般是圖片地址,但是要注意,這個圖片地址不只能在此防盜鏈規則里,否則也訪問不到。
對 nginx 配置不熟悉的同學請參考 windows 下配置 Nginx 常見問題
3.自定義 HttpHandler 處理
方法步驟: 1 創建自定義 handlers 代碼如下,根據 Referre 判斷請求來源,如果符合標准,輸出文件流,否則停止響應。也可以輸出一個特定的圖片。
namespace WeiXinDemo.Globals { /// <summary> /// 測試 Handler 實現圖片防盜鏈 /// </summary> public class MyImgHandler : IHttpHandler { public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { var response = context.Response; var request = context.Request; if (request.UrlReferrer == null || !WebApplication.ImgHost.Equals(request.UrlReferrer.Host)) { response.End(); return; } var fileName = context.Server.MapPath(request.FilePath); response.WriteFile(fileName); if (request.UrlReferrer == null || WebApplication.ImgHost.Equals(request.UrlReferrer.Host)) { response.WriteFile(fileName); } else { response.End(); } } } }
2 在web.config 文件 handlers 節點下添加自定義 Handler,滿足要求的請求進入 WeiXinDemo.Globals.MyImgHandler 進行處理
<system.webServer> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<!-- 這是添加的自定義Handler --> <add name="jpgHandler" path="*.jpg" verb="*" type="WeiXinDemo.Globals.MyImgHandler,WeixinDemo" />
<add name="pngHandler" path="*.png" verb="*" type="WeiXinDemo.Globals.MyImgHandler,WeixinDemo" />
<add name="bmpHandler" path="**.bmp" verb="*" type="WeiXinDemo.Globals.MyImgHandler,WeixinDemo" />
<add name="gifHandler" path="*.gif" verb="*" type="WeiXinDemo.Globals.MyImgHandler,WeixinDemo" /> </handlers> </system.webServer>
4. 通過 MVC 自定義路由規則防盜鏈
首先我們要在 web.config 文件里 system.webServer 節點下 設置<modules runAllManagedModulesForAllRequests="true" /> 同時還要在 RouteConfig.cs 文件里添加 routes.RouteExistingFiles = true;確保所有路由都通過 RouteCollection 匹配 。
在這里我們需要了解 UrlRoutingModule,它是System.Web.Routing的一部分。UrlRoutingModule用於檢驗請求的url和本地硬盤 中的文件能不能相匹配。如果匹配,則交給IIS處理。如果不匹配它會檢驗 RouteCollection 來決定能不能繼續傳遞請求。而設置了 runAllManagedModulesForAllRequests="true" 后,會改變默認行為,所有請求都須要 運用 Routing來處理。
<system.webServer> <validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true" /> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>
配置文件設置好以后添加自定義路由規則,下面是自定義路由規則的實現代碼,其實里面就做了一件事,使用正則表達式判斷當前請求是否符合規則,如果符合規則,則進入指定的處理頁面,否則去匹配其他的路由規則。
namespace WeiXinDemo.Globals { /// <summary> /// 圖片路由規則(自定義) /// </summary> public class ImgRouteRule : IRouteConstraint { public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { var regex = new Regex(@"/[^/]+(.jpg|.bmp|.gif|.png)"); var result = regex.IsMatch(httpContext.Request.RawUrl); return result; } } }
這樣就造成了一個問題,所有的請求(比如 .css .js .htm 等等)都去路由規則里面去匹配,如果在路由規則里面匹配不到那么就會返回 404,如何避免呢?通過 RouteConfig.cs 文件配置忽略。
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { //確保所有路由都通過 RouteCollection 匹配(圖片防盜鏈) routes.RouteExistingFiles = true; //忽略 json,html,js,css文件 routes.IgnoreRoute("{*pathInfo}", new { pathInfo = @".+(.json|.html|.js|.css)" }); routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //符合路由規則的轉到控制器 ImgRule/Index 處理 (自定義路由規則實現 圖片防盜鏈) routes.MapRoute( name: "ImagesRoute", url: "{*catchall}", defaults: new { controller = "ImgRule", action = "Index" }, // ImgRouteRule 為自定義路由規則,符合此規則,進入路由 訪問 ImgRule/Index constraints: new { customConstraint = new ImgRouteRule() }, //控制器類命名空間 namespaces: new[] { "WeiXinDemo.Controllers" }); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
在上面的代碼里配置了 "ImagesRoute" 的路由,使用的自定義路由規則,當滿足規則時,跳轉到 ImgRule/Index 去處理,處理代碼跟使用 HttpHandler 類似
public class ImgRuleController : Controller { // GET: ImgRule public FileStreamResult Index() { var fPath = Server.MapPath("~" + Request.FilePath); if (Request.UrlReferrer == null) return null; if (!System.IO.File.Exists(fPath) || !WebApplication.ImgHost.Equals(Request.UrlReferrer.Host) || !WebApplication.ImgHost.Equals(Request.UrlReferrer.Host)) return null; return GetFile(fPath); } private FileStreamResult GetFile(string fPath) { return File(new FileStream(fPath, FileMode.Open, FileAccess.Read), GetContentType(Request.FilePath)); } private static string GetContentType(string url) { switch (Path.GetExtension(url)) { case ".gif": return "Image/gif"; case ".jpg": return "Image/jpeg"; case ".png": return "Image/png"; default: break; } return null; } }
5. 通過 MVC 自定義 RouteHandler 防盜鏈
注意這里是自定義路由,別跟第4種方法混淆了,這里是指定自定義路由處理圖片。
1 web.config 文件配置同第4種方法,也要開啟 runAllManagedModulesForAllRequests="true"
2 創建自定義路由,自定義路實現代碼如下 ImageRouteHandler ,同時還有自定義路由調用的 HttpHandler ,ImageHandler
using System.IO; using System.Text.RegularExpressions; using System.Web; using System.Web.Routing; namespace WeiXinDemo.Globals { /// <summary> /// 測試自定義 RouteHandler 圖片防盜鏈 /// </summary> public class ImageRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new ImageHandler(); } } /// <summary> /// 自定義路由調用的 HttpHandler /// </summary> public class ImageHandler : IHttpHandler { public ImageHandler() { } public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext context) { var response = context.Response; var request = context.Request; if (request.UrlReferrer == null || !WebApplication.ImgHost.Equals(request.UrlReferrer.Host)) { response.End(); return; } var fileName = context.Server.MapPath(request.FilePath); response.WriteFile(fileName); } } }
RouteConfig.cs 文件配置 如下,這里指定了 對根目錄下的 jpg 文件的訪問進入指定路由處理程序 ImageRouteHandler。 其實這里可以把圖片都放在某一個特定文件夾下,然后對這個文件夾下文件的訪問做放盜鏈。
namespace WeiXinDemo { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { //確保所有路由都通過 RouteCollection 匹配(圖片防盜鏈) routes.RouteExistingFiles = true; //忽略 json,html,js,css文件 routes.IgnoreRoute("{*pathInfo}", new { pathInfo = @".+(.json|.html|.js|.css)" }); routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //圖片防盜鏈 routes.Add("ImagesRoute", new Route("{name}.jpg", new ImageRouteHandler())); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } } }
6. 通過 HttpModule 防盜鏈
1. 修改 web.config 配置文件
<system.webServer> <modules runAllManagedModulesForAllRequests="true" > <add name="ImgModule" type="WeiXinDemo.Globals.ImageModel,WeiXinDemo"/> </modules> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> </handlers> </system.webServer>
2. 創建實現 IHttpModule 接口的 ImageModel 類
using System; using System.Text.RegularExpressions; using System.Web; namespace WeiXinDemo.Globals { /// <summary> /// 測試 HttpModel 圖片防盜鏈 /// </summary> public class ImageModel : IHttpModule { public void Dispose() { }
public void Init(HttpApplication context) { context.BeginRequest += new EventHandler(BeginRequest); } void BeginRequest(object sender, EventArgs e) { HttpApplication context = (HttpApplication)sender; var regex = new Regex(@"/[^/]+(.jpg|.bmp|.gif|.png)"); var request = context.Context.Request; if (!regex.IsMatch(request.RawUrl)) return; if (request.UrlReferrer == null || !WebApplication.ImgHost.Equals(request.UrlReferrer.Host)) { context.Context.Response.End(); return; } var fileName = context.Context.Server.MapPath(request.FilePath); context.Context.Response.WriteFile(fileName); } } }
3. RouteConfig.cs 文件忽略不需要防盜鏈的靜態資源
using System.Web.Mvc; using System.Web.Routing; namespace WeiXinDemo { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { //確保所有路由都通過 RouteCollection 匹配(圖片防盜鏈) routes.RouteExistingFiles = true; //忽略 json,html,js,css文件 routes.IgnoreRoute("{*pathInfo}", new { pathInfo = @".+(.json|.html|.js|.css)" }); routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } } }
7. 涉及知識
本文只做了一件事情,圖片防盜鏈,其實從不同的實現方式來看它涉及到不同的知識。
1. URL Rewrite Module 組件的使用
Creating Rewrite Rules for the URL Rewrite Module
2. Nginx
3. IIS 工作原理,asp.net 管線
ASP.NET MVC請求處理管道生命周期的19個關鍵環節(1-6)
4. Mvc UrlRouting 處理機制
本文只是在這里探討了一下實現防盜鏈的方法,沒有考慮性能的問題,如果考慮性能跟簡便性,我個人喜歡用 第 1 和第 2種實現方式,第 3種 次之。 條條大路通羅馬,就看那種方法最適合。
