IHttpHandler的妙用(2):防盗链!我的资源只有我的用户才能下载

简介:
 昨天粗略讲了一下IHttpHandler接口的作用和动态给图片添加水印的处理,如果对这些不太清除的朋友,建议看看这篇《IHttpHandler的妙用(1):给图片添加水印》: http://blog.csdn.net/zhoufoxcn/archive/2008/01/10/2033530.aspx
昨天也提到了IHttpHandler接口主要有一个IsReusable属性和一个ProcessRequest方法,利用这个方法我们可以处理很多事情的,昨天我们利用了这个方法给图片动态添加了水印,今天我再来展示另一种用法。
 
大家查看一个msdn,可以看到它的声明如下:
Visual Basic(声明) 
Sub ProcessRequest ( _
    context As HttpContext _
)
 
Visual Basic(用法) 
Dim instance As IHttpHandler
Dim context As HttpContext

instance.ProcessRequest(context)
 
C# 
void  ProcessRequest (
    HttpContext context
)
注意这个HttpContext对象,它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session 和 Server)的引用。 
有了它我们就方便多了,因为我们的下载资源一般都会有一个下载介绍(假设为details.aspx?id=***),用户查看介绍之后,如果愿意下载,就会点击下载链接,这个链接也是一个页面(假设为download.aspx?id=***),我们就可以得出结论,只要是用户通过我们的网站下载这些资源,那么在下载资源之前访问那个页面(简称前导页,下同)一定是details.aspx,因此我们就可以得出结论只要是下载之前的前导页不是details.aspx这个页面,那个这个下载请求一定是别的网站盗链(其实还可以放宽一点,在下载之前的前导页一定是本站的页面,也还可以要求更紧一点,下载之前访问的页面的id值一定要与下载的id值一致,这就看大家的实际要求了)!
有了这个推论之后,我们就可以动手写代码了:
using  System;
using  System.IO;
using  System.Web;

///   <summary>
///  说明:DownloadHandler是一个防盗链的类,它可以防止本站资源被别的网站盗用
///  作者:周公
///  日期:2008-1-11
///  首发地址: http://blog.csdn.net/zhoufoxcn
///   </summary>
public   class  DownloadHandler:IHttpHandler
{
    
public  DownloadHandler()
    {
        
//
        
//  TODO: 在此处添加构造函数逻辑
        
//
    }

    
#region  IHttpHandler 成员
    
///   <summary>
    
///  指示IHttpHandler 实例是否可再次使用
    
///   </summary>
     public   bool  IsReusable
    {
        
get  {  return   true ; }
    }
    
///   <summary>
    
///  处理请求的方法
    
///   </summary>
    
///   <param name="context"> 它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session 和 Server)的引用。 </param>
     public   void  ProcessRequest(HttpContext context)
    {
        Uri referrerUri 
=  context.Request.UrlReferrer; // 获取下载之前访问的那个页面的uri
        Uri currentUri  =  context.Request.Url;
        
if  (referrerUri  ==   null ) // 没有前导页,直接访问下载页
        {
            
// 输出提示,可以根据自身要求完善此处代码
            context.Response.Write( " 请不要盗链本站资源,请从首页访问。<a href='index.aspx'>首页</a> " );
            
return ;
        }
        
#region  判断前导页是否位于本站可以用此段代码
        
// if (referrerUri.Host == currentUri.Host) // 前导页和当前请求页位于同一个主机
        
// {
        
//      // 用户是通过正常路径访问的,向用户提供下载文件
        
//      // 实际情况是根据id从数据库找到文件的物理路径,然后输出
        
//      // 为了简单代码,仅仅演示流程,这里我直接输出了文件
        
//      // 周公注。2008-1-11
        
//      // 获取请求的物理文件路径
        
//     WriteFile(context);
        
// }
        
// else
        
// {
        
//      // 输出提示,可以根据自身要求完善此处代码
        
//     context.Response.Write("请不要盗链本站资源,请从首页访问。<a href='index.aspx'>首页</a>");
        
// }
         #endregion
        
#region  判断前导页是否是我们的介绍页面
        
string  referrerPage  =  referrerUri.LocalPath.Substring(referrerUri.LocalPath.LastIndexOf( ' / ' ) + 1 );
        
if  (referrerPage  ==   " Details.aspx " ) // 如果前导页是我们的介绍页面
        {
            
// 用户是通过正常路径访问的,向用户提供下载文件
            
// 实际情况是根据id从数据库找到文件的物理路径,然后输出
            
// 为了简单代码,仅仅演示流程,这里我直接输出了文件
            
// 周公注。2008-1-11
            
// 获取请求的物理文件路径
            WriteFile(context);
        }
        
else
        {
            
// 输出提示,可以根据自身要求完善此处代码
            context.Response.Write( " 请不要盗链本站资源,请从首页访问。<a href='index.aspx'>首页</a> " );
        }
        
#endregion

    }

    
private   void  WriteFile(HttpContext context)
    {
        
// 用户是通过正常路径访问的,向用户提供下载文件
        
// 实际情况是根据id从数据库找到文件的物理路径,然后输出
        
// 为了简单代码,仅仅演示流程,这里我直接输出了文件
        
// 周公注。2008-1-11
        
// 获取请求的物理文件路径
         string  path  =  context.Request.PhysicalPath;
        
// 注意这里rar文件的ContentType是application/octet-stream
        
// 不同格式文件的contentType有可能不同
        context.Response.ContentType  =   " application/octet-stream " ;
        context.Response.WriteFile(path);
    }

    
#endregion
}
对web.config的配置:
<? xml version="1.0" ?>
<!--  
    注意: 除了手动编辑此文件以外,您还可以使用 
    Web 管理工具来配置应用程序的设置。可以使用 Visual Studio 中的
     “网站”->“Asp.Net 配置”选项。
    设置和注释的完整列表在 
    machine.config.comments 中,该文件通常位于 
    WindowsMicrosoft.NetFrameworkv2.xConfig 中
-->
< configuration >
    
< appSettings >
        
<!-- 添加到图片上的水印文字 -->
        
< add  key ="WaterMark"  value ="http://blog.csdn.net/zhoufoxcn" />
        
<!-- 水印文字的字体大小 -->
        
< add  key ="Font-Size"  value ="72" />
    
</ appSettings >
    
< connectionStrings />
    
< system .web >
        
<!--  
            设置 compilation debug="true" 将调试符号插入
            已编译的页面中。但由于这会 
            影响性能,因此只在开发过程中将此值 
            设置为 true。
        
-->
        
< compilation  debug ="true" />
        
<!--
            通过 <authentication> 节可以配置 ASP.NET 使用的 
            安全身份验证模式,
            以标识传入的用户。 
        
-->
        
< authentication  mode ="Windows" />
        
<!--
            如果在执行请求的过程中出现未处理的错误,
            则通过 <customErrors> 节可以配置相应的处理步骤。具体说来,
            开发人员通过该节可以配置
            要显示的 html 错误页
            以代替错误堆栈跟踪。

        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
            <error statusCode="403" redirect="NoAccess.htm" />
            <error statusCode="404" redirect="FileNotFound.htm" />
        </customErrors>
        
-->
        
< httpHandlers >
      
<!-- 只处理UploadImages目录下的jpg文件,别的目录下的图片不处理 -->
            
< add  path ="UploadImages/*.jpg"  verb ="*"  type ="ImageHandler" />
      
<!-- 所有的对zip、rar、iso文件的请求都由DownloadHandler处理 -->
      
< add  path ="*.zip"  verb ="*"  type ="DownloadHandler" />
      
< add  path ="*.rar"  verb ="*"  type ="DownloadHandler" />
      
< add  path ="*.iso"  verb ="*"  type ="DownloadHandler" />
        
</ httpHandlers >
    
</ system.web >
</ configuration >
前导页(Details.aspx):
<% @ Page Language = " C# "  AutoEventWireup = " true "  CodeFile = " Details.aspx.cs "  Inherits = " Details "   %>

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >

< html  xmlns ="http://www.w3.org/1999/xhtml"   >
< head  runat ="server" >
    
< title > 无标题页 </ title >
</ head >
< body >
    
< form  id ="form1"  runat ="server" >
    
< div >
    
< href ="Download.aspx?id=1" > 下载 </ a >
    
</ div >
    
</ form >
</ body >
</ html >
下载页没有什么html代码,主要是后台代码。下载页后台代码(Download.aspx.cs):
using  System;
using  System.Data;
using  System.Configuration;
using  System.Collections;
using  System.Web;
using  System.Web.Security;
using  System.Web.UI;
using  System.Web.UI.WebControls;
using  System.Web.UI.WebControls.WebParts;
using  System.Web.UI.HtmlControls;

public   partial   class  Download : System.Web.UI.Page
{
    
protected   void  Page_Load( object  sender, EventArgs e)
    {
        
if  ( ! Page.IsPostBack)
        {
            CheckUser();
            
// 实际情况是根据id从数据库找到文件的物理路径,然后输出
            
// 为了简单代码,仅仅演示流程,这里我直接输出了文件
            
// 周公注。2008-1-11
            
// 首发地址: http://blog.csdn.net/zhoufoxcn
            Response.Redirect( " download/demo.rar " );//这里就会交给DownloadHanddler处理了
        }
    }
    
// 检查用户登录等信息
     private   void  CheckUser()
    {
        
// 仅仅演示,具体代码根据具体要求编写
        
// 比如用户没有登录如何处理,用户没有下载权限如何处理等
         return ;
    }
}
正常流程截图:
(一)先打开介绍页面:

(二)点击下载链接进入下载页面:

此时出现文件保存对话框,情况正常。
再看盗链情况(非正常情况):
(一)打开首页(非前导页,跳过这一步也没有关系)

(二)直接访问下载页面或者直接访问下载文件的实际url地址:

此时出现了我们自定义的错误提示,甚至地址栏上都出现了该文件的物理地址,可是就是没有办法下载,此时就算是利用迅雷等下载软件,下载到的也是一个无效的文件,如图:

这样就达到我们的目地了,不是通过正常途径来下载是没有办法下载到他需要的文件的。
 
顺便说一下,写作本文时我是边编写代码边测试的(本博客原创的代码都是如此,测试通过之后方才发表,所以一般不会有什么问题,如果你学习的过程中发现有任何错误,请仔细检查你的代码,因为各人本地环境不同,出错原因各异,恕本人不一一指出你代码的错误之处)。













本文转自周金桥51CTO博客,原文链接: http://blog.51cto.com/zhoufoxcn/167036,如需转载请自行联系原作者


相关文章
|
1月前
|
缓存 Ubuntu JavaScript
踩坑记录:QML加载图片资源
踩坑记录:QML加载图片资源
85 0
|
7天前
|
小程序 开发者
体验版小程序为何无法访问云端服务器后端接口(请求失败...(已完美解决附加图片))?
体验版小程序为何无法访问云端服务器后端接口(请求失败...(已完美解决附加图片))?
14 0
|
5月前
|
编解码 安全 Java
互联网并发与安全系列教程(10) -基于URL转码方式实现API设计
互联网并发与安全系列教程(10) -基于URL转码方式实现API设计
32 0
|
2月前
|
文字识别 小程序 开发工具
mPaaS小程序问题之调用选图片的时候报错如何解决
mPaaS小程序是阿里巴巴移动平台服务(mPaaS)推出的一种轻量级应用解决方案,旨在帮助开发者快速构建跨平台的小程序应用;本合集将聚焦mPaaS小程序的开发流程、技术架构和最佳实践,以及如何解决开发中遇到的问题,从而助力开发者高效打造和维护小程序应用。
38 0
|
3月前
|
前端开发
elementui-upload组件自定义样式上传(upload中常用的属性,但是网络上却找不到教程)(解决bug删除之后再次上传会上传删除的图片)专注后端工程师的前端速成
elementui-upload组件自定义样式上传(upload中常用的属性,但是网络上却找不到教程)(解决bug删除之后再次上传会上传删除的图片)专注后端工程师的前端速成
67 0
|
9月前
|
PHP
PHP实现自制随机图片API- 调用文件夹和引用网络图片
PHP实现随机图片API- 调用文件夹和引用网络图片
112 0
|
11月前
|
小程序 前端开发 JavaScript
小程序上传多张图片到springboot后台,返回可供访问的图片链接
小程序上传多张图片到springboot后台,返回可供访问的图片链接
11628 0
|
缓存 CDN
yii2.0网站首页有很多的文章封面图片,增加了很多的http请求,如何进行性能优化?底层原理是什么?
yii2.0网站首页有很多的文章封面图片,增加了很多的http请求,如何进行性能优化?底层原理是什么?
|
安全 CDN
github,codeforces访问过慢处理方法(涉及权限)
更新:最近发现下列配置已经失效,但是github的访问速度明显快乐很多,所以不需要哦配置,如果找到正确的网站或者其他网站访问慢可以按照相同的方法。另外。我之前的始用权限问题比较麻烦,可以直接右击属性安全中设置应用程序的权限可以修改内容(记得改回去,万一被黑了然后黑客把你的hosts文件链向黄网?)
794 0
github,codeforces访问过慢处理方法(涉及权限)
|
API 数据安全/隐私保护
掘金外链即将失效?论如何用脚本一次性下载/替换失效的外链图片
前言 大约一个月前,微博的图片外链失效了,以及掘金因为盗链问题也于2019/06/06决定开启防盗链,造成的影响是:个人博客网站的引用了这些图片外链都不能显示。 目前微博和掘金的屏蔽,在CSDN和segmentfault都是可以正常显示的,只影响个人博客。 比如前段时间我的博客:obkoro1.com上引用的微博图片都不能显示了。 因为我写博客比较频繁,被屏蔽的图片不在少数,肯定不能一个个手动的替换,查了一番没有找到现成的解决方案,做了个脚本工具,并且写了文档把它开源出来了。 markdown-img-down-site-change(下载/替换markdown中的图片)
246 0
掘金外链即将失效?论如何用脚本一次性下载/替换失效的外链图片