基于Refferer的防盗链方案

google、baidu的图片搜索中常看到有些图片上面写着诸如“此图片仅供……用户交流”,说明这些网站是已经发现了我们浏览器发出的请求为站外链接(而且是非伙伴站点的链接)。

防盗链有很多方案,比如一些网盘经常会对下载链接做手脚,给用户看到的链接都是处理过的,像skydriver生成的下载链接中就会包含一个时间戳的加密字符串,你拿到的url链接过了一天就废掉了(我也是刚发现这个问题,以前给别人的链接都不能用了。。。)。

不过本篇谈的是防图片盗链,图片资源在一个站点上被访问的频率应该是相当多的,如果采用timespan、token之类的方案对性能也是一种浪费,所以应该采用速度快又能防住大多数盗链情形的方法。我们模仿sina这种大站的做法,通过判断request响应头中的Refferer来筛选盗链连接。

(注意:Refferer是很容易被伪造的,所以对于重要资源还是应该使用其他方案)

效果预览

imageimage image

图一:站内引用图片资源,能够正常显示。图二:浏览器直接输入图片地址,被重定向到反盗链提示页面。图三:站外引用,被重定向到反盗链页面。

实现

a.通过自定义HttpHandler来处理对指定类型文件的处理,在ProcessRequest(HttpContext context)方法中判断Refferer,如果Refferer为空,说明是直接访问(比如用户将图片地址直接粘贴在地址栏中访问),如果Refferer的域名与Request中的域名相同说明是站内引用,如果Refferer的域名是伙伴站点,那么也应该允许,除此以外就应该拒绝。

b.关于参数配置:为实现逻辑与具体数据、参数之间的解耦,我们可以把伙伴站点列表以及重定向地址放在web.config配置文件中,这可以通过自定义ConfigurationSection来实现。

代码

CheckReffererHandler

CheckReffererHandler
namespace  Sopaco.Library.Web.AntiForgery
{
    
///   <summary>
    
///  检查是否为外站连接
    
///   </summary>
     public   class  CheckReffererHandler : IHttpHandler, IRequiresSessionState
    {
        
#region  Fields
        
private   string  _redirectPath  =   null ;
        
private   static  IEnumerable < string >  _acceptedHostList;
        
#endregion

        
#region  Constructors & Initializer
        
public  CheckReffererHandler()
        {
            Init();
        }
        
private   void  Init()
        {
            
if (_acceptedHostList  ==   null )
                rebuildAcceptedList();
        }
        
#endregion

        
#region  IHttpHandler 成员

        
public   bool  IsReusable
        {
            
get  {  return   false ; }
        }

        
public   void  ProcessRequest(HttpContext context)
        {
            Uri uri 
=  context.Request.UrlReferrer;
            
if  (uri  ==   null ) // 如果是直接引用
                SendBackToForgery(context);
            
else   if  (CheckIsHostSame(uri, context.Request.Url)) // 如果是站内引用
                SendAcceptBack(context, context.Request.Path);
            
else   if (CheckFromAcceptedHost(uri)) // 对于站外引用,检查是否来源于伙伴站点
                SendAcceptBack(context, context.Request.Path);
            
else // 对于非伙伴站点的处理
                SendBackToForgery(context);
        }

        
#endregion
        
#region  protected Helper Methods
        
private   bool  CheckFromAcceptedHost(Uri uri)
        {
            
return  _acceptedHostList.Where(p  =>  p.Equals(uri.Host, StringComparison.InvariantCultureIgnoreCase)).Count()  >   0 ;
        }

        
private   bool  CheckIsHostSame(Uri host0, Uri host1)
        {
            
return  host0.Host.Equals(
                host1.Host, StringComparison.InvariantCultureIgnoreCase);
        }
        
private   void  SendBackToForgery(HttpContext context)
        {
            
if (_redirectPath  ==   null )
            {
                _redirectPath 
=  ConfigurationManager.AppSettings[ " backToForgeryUrl " ];
                
if  (_redirectPath  ==   null )
                {
                    context.Response.StatusCode 
=   404 ;
                    
return ;
                }
            }
            context.Response.Redirect(_redirectPath);
        }
        
private   void  SendAcceptBack(HttpContext context,  string  virtualPath)
        {
            
string  filePath  =  context.Server.MapPath(virtualPath);
            
if  ( ! File.Exists(filePath))
            {
                context.Response.StatusCode 
=   404 ;
                context.Response.StatusDescription 
=   " 未找到文件: "   +  virtualPath;
                
return ;
            }
            context.Response.WriteFile(filePath);
        }
        
private   static   void  rebuildAcceptedList()
        {
            SimpleSection section 
=  ConfigurationManager.GetSection( " checkReffererHandler " as  SimpleSection;
            _acceptedHostList 
=  section.Items.Items.Select(p  =>  p.Value);
        }
        
#endregion
    }
}

 

配置文件部分节选

 

web.config节选
<!-- 声明自定义配置节  -->
    
< section name = " checkReffererHandler "  
             type
= " Sopaco.Library.EnterLib.ConfigurationSections.SimpleSection, Sopaco.Library.EnterLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null "  
             allowLocation
= " true "  allowDefinition = " Everywhere "  allowExeDefinition = " MachineToApplication "  restartOnExternalChanges = " true "  
             requirePermission
= " true "   />

<!-- 配置数据 -->
  
< checkReffererHandler >
    
< items >
      
<!-- 以下为允许外站引用的站点清单 -->
      
< add name = " 1 "  value = " www.sopaco.net "   />
      
< add name = " 2 "  value = " www.cnblogs.com "   />
      
< add name = " 3 "  value = " live.spaces.com "   />
    
</ items >
  
</ checkReffererHandler >

 

自定义配置节相关代码

 

自定义配置节相关代码
namespace  Sopaco.Library.EnterLib.ConfigurationSections
{
    
public   class  SimpleSection : ConfigurationSection
    {
        [ConfigurationProperty(
" items " )]
        [ConfigurationCollection(
typeof (SimpleItemsElement))]
        
public  SimpleItemsElement Items
        {
            
get  {  return   base [ " items " as  SimpleItemsElement; }
            
set  {  base [ " items " =  value; }
        }
    }
}
namespace  Sopaco.Library.EnterLib.ConfigurationSections
{
    
public   class  SimpleItemsElement : ConfigurationElementCollection
    {
        
#region  Fields
        List
< SimpleItemElement >  _items;
        
#endregion
        
protected   override  ConfigurationElement CreateNewElement()
        {
            
return   new  SimpleItemElement();
        }

        
protected   override   object  GetElementKey(ConfigurationElement element)
        {
            
return  (element  as  SimpleItemElement).Name;
        }

        
public  IList < SimpleItemElement >  Items
        {
            
get
            {
                
if  (_items  ==   null )
                {
                    _items 
=   new  List < SimpleItemElement > ();
                    RefreshItems();
                }
                
return  _items;
            }
        }

        
public   void  RefreshItems()
        {
            _items.Clear();
            
for ( int  i  =   0 ; i  <   base .Count; i += 1 )
            {
                _items.Add(
base .BaseGet(i)  as  SimpleItemElement);
            }
        }
    }
}
namespace  Sopaco.Library.EnterLib.ConfigurationSections
{
    
public   class  SimpleItemElement : ConfigurationElement
    {
        [ConfigurationProperty(
" name " )]
        
public   string  Name
        {
            
get  {  return   base [ " name " as   string ; }
            
set  {  base [ " name " =  value; }
        }
        [ConfigurationProperty(
" value " )]
        
public   string  Value
        {
            
get  {  return   base [ " value " as   string ; }
            
set  {  base [ " value " =  value; }
        }
    }
}

 

源代码下载链接:

 http://files.cnblogs.com/wJiang/refferer.rar

你可能感兴趣的:(FF)