首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET MVC 自訂 RouteHandler 防止圖片盜鏈實作

     編輯:關於ASP.NET

透過自訂 IRouteHandler 攔截圖片請求,驗證 Referer 標頭判斷是否為盜鏈,非法請求回傳 403 或預設圖片,保護伺服器頻寬。

ASP.NET MVC 路由處理流程

  1. IIS 接收請求 → 轉交 ASP.NET
  2. URL 路由模組 (UrlRoutingModule) 依路由表匹配
  3. 匹配成功的 Route 透過其 RouteHandler 取得 IHttpHandler
  4. Handler 執行 ProcessRequest 產生回應

MVC 路由處理流程圖

自訂 AntiHotlinkRouteHandler

public class AntiHotlinkRouteHandler : IRouteHandler {
    private readonly string[] _allowedDomains;
    private readonly string _fallbackImageVirtualPath;
    
    public AntiHotlinkRouteHandler(string[] allowedDomains, string fallbackImageVirtualPath) {
        _allowedDomains = allowedDomains ?? new string[0];
        _fallbackImageVirtualPath = fallbackImageVirtualPath;
    }
    
    public IHttpHandler GetHttpHandler(RequestContext requestContext) {
        var context = requestContext.HttpContext;
        var referer = context.Request.UrlReferrer;
        
        // 允許條件:無 Referer (直接輸入網址)、同域、白名單域名
        bool isAllowed = referer == null 
            || referer.Host.Equals(context.Request.Url.Host, StringComparison.OrdinalIgnoreCase)
            || _allowedDomains.Any(d => referer.Host.EndsWith(d, StringComparison.OrdinalIgnoreCase));
        
        if (!isAllowed) {
            // 盜鏈:回傳 403 或重導向至預設圖片
            if (!string.IsNullOrEmpty(_fallbackImageVirtualPath)) {
                context.Response.Redirect(_fallbackImageVirtualPath);
            } else {
                context.Response.StatusCode = 403;
                context.Response.StatusDescription = "Forbidden: Hotlinking not allowed";
                context.Response.End();
            }
            return new EmptyHandler(); // 結束處理
        }
        
        // 合法請求:交由靜態檔案處理器或自訂 Handler
        return new StaticFileHandler(); // 或實作輸出圖片邏輯
    }
}

// 空 Handler 避免後續處理
public class EmptyHandler : IHttpHandler {
    public bool IsReusable => true;
    public void ProcessRequest(HttpContext context) { }
}

路由註冊 (Global.asax)

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
    // 圖片防盜鏈路由:攔截 /images/ 目錄下所有請求
    var allowed = new[] { "partner.com", "cdn.mysite.com" };
    var fallback = "/Content/Images/hotlink-denied.png";
    
    routes.Add("ImageAntiHotlink", new Route(
        "images/{*path}",
        new RouteValueDictionary(),
        new AntiHotlinkRouteHandler(allowed, fallback)
    ));
    
    // 預設 MVC 路由
    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

進階:輸出原圖而非重導向

public class ImageDeliveryHandler : IHttpHandler {
    public void ProcessRequest(HttpContext context) {
        string virtualPath = "~/images/" + context.Request.RequestContext.RouteData.Values["path"];
        string physicalPath = context.Server.MapPath(virtualPath);
        
        if (!File.Exists(physicalPath)) {
            context.Response.StatusCode = 404;
            return;
        }
        
        // 設定快取標頭
        context.Response.Cache.SetCacheability(HttpCacheability.Public);
        context.Response.Cache.SetMaxAge(TimeSpan.FromDays(30));
        context.Response.Cache.SetLastModified(File.GetLastWriteTimeUtc(physicalPath));
        
        // 輸出檔案
        context.Response.ContentType = GetMimeType(Path.GetExtension(physicalPath));
        context.Response.WriteFile(physicalPath);
    }
    
    private string GetMimeType(string ext) {
        var map = new Dictionary {
            {".jpg", "image/jpeg"}, {".jpeg", "image/jpeg"},
            {".png", "image/png"}, {".gif", "image/gif"},
            {".webp", "image/webp"}, {".svg", "image/svg+xml"}
        };
        return map.TryGetValue(ext.ToLower(), out string mime) ? mime : "application/octet-stream";
    }
    
    public bool IsReusable => true;
}

在 AntiHotlinkRouteHandler 中合法請求回傳 new ImageDeliveryHandler()。

IIS 7+ 整合模式注意事項

  • 確保 runAllManagedModulesForAllRequests="true" 或在 system.webServer/modules 註冊 UrlRoutingModule
  • 靜態檔案模組 (StaticFileModule) 預設優先處理實體檔案,路由需在其之前或針對虛擬路徑
  • 生產環境建議結合 web.config <rewrite> 規則或 CDN 防盜鏈設定,效能更佳

替代方案比較

方案優點缺點
自訂 RouteHandler整合 MVC 路由、程式碼可控、支援複雜邏輯所有圖片請求經過 Managed Pipeline、效能稍低
IIS URL Rewrite 模組原生模組、效能極高、正則表達式靈活規則寫在 web.config、複雜邏輯難維護
CDN 防盜鏈 (如 Cloudflare, Azure CDN)邊緣攔截、不消耗源站頻寬、設定簡單需使用 CDN 服務、成本較高
HttpModule 攔截全域攔截、可處理所有副檔名影響所有請求、需小心效能
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved