技术开发 频道

用ASP.NET实现文件的保护性下载高级篇

  【IT168 专稿】许多时候,我们需要在因特网上提供文件下载服务,但是又要防止未经授权的下载,这时该怎么办?在上一篇中,我们为读者详细介绍一种使用ASP.NET实现的HTTP处理程序的解决方案的基本保护,在本文中,我们将介绍更详尽的保护措施。

  一、更详尽的保护措施

  我们的目标是,让授权用户能够浏览一个包含了已经购买的软件的页面,并且通过单击其中的链接就可以下载特定的商品。列出产品时,可以使用表结构,但是如何来保护链接呢? 之前我们介绍的方法只是防止匿名用户下载压缩文件,但是现在我们要防止授权用户直接浏览压缩文件。为此,我们需要编写一个定制的处理程序。

  定制的HTTP处理程序是实现了IHttpHandler接口的类。这个接口定义了一个称为ProcessRequest的方法,以及一个Boolean类型的名为IsReusable的属性。该属性决定了其它请求是否能够利用同一个处理程序,所以这里简单返回一个真值。这个方法将会收到一个HttpContext类型的参数。这变量为我们提供了访问该请求整个上下文的权限,包括请求中的信息和订制另一个方向上的请求的方法。

  现在,我们要创建一个称为FileDenialHandler的处理程序,它的作用是停止一个请求,并将用户重定向到一个页面来通知他们访问被拒绝。当这个处理程序取得该请求的时候,就会调用ProcessRequest方法并且执行重定向。

public void ProcessRequest(HttpContext context)
   {
   context.Response.Redirect(
      
"~/Downloads/Files/AccessDenied.aspx");
   }

  如您所见,这个页面位于根目录的Downloads/Files文件夹中,完整的FileDenialHandler.cs处理程序如下所示:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
namespace DotNetDude.Web.UI
{
  
public class FileDenialHandler : IHttpHandler
   {
      
public void ProcessRequest(HttpContext context)
      {
         context.Response.Redirect(
            
"~/Downloads/Files/AccessDenied.aspx");
      }

      
public bool IsReusable
      {
        
get
         {
            
return true;
         }
      }
   }
}

  现在,这个FileDenialHandler类什么也没做,所以必须将其写入站点。为了这样,我们将其放入web.config文件的中。

  文件web.config列出了配置部分的所有特殊处理程序,并为其规定了相应的信息,包括实例化这个处理程序的谓词,匹配要让这个处理程序来处理的文件的通配符路径,以及用于该定制的处理程序的类型定义。在这个例子中,添加的配置部分如下所示:

   <httpHandlers>
  
<add verb="*" path="*.zip"
   type
="DotNetDude.Web.UI.
   FileProtectionHandler,
   DotNetDude.HttpHandlers"
/>
  
</httpHandlers>

  参数type是标准的.NET完全限定名写法,并通过逗号组合了一个组件程序集名称。在实际的Web项目中编写处理程序的时候,可以省去程序集名称。

  现在这个条目将所有的压缩文件请求转发给新的处理程序,因此立即将请求重定向到“Access Denied”页面。即使在本例中跳过该IIS条目,它仍然会照常工作,因为这些事情都交由我们的处理程序了。 然而,我们想要的效果是让系统在判定匿名用户访问非法的ZIP 文件之前,首先把他们导航至登录页面。

  如果检查框架目录中的web.config文件,就会发现一列处理常见文件扩展名用于ASP.NET的处理程序。定义这个文件中的部分的处理程序决定了IIS如何恰当地转发ASPX页面、ASMX Web 服务以及所有其他文件。 这个处理程序列表还定义了哪些文件扩展名是禁用的,例如*.config。事实上,可以利用一个称为HttpForbiddenHandler的处理程序来禁用所有以扩展名.config结尾的文件,并自动显示一个“HTTP 403error Forbidden :Access is denied ”页面。

  所以您可能问,无什么不只用Microsoft提供的处理程序来处理压缩文件呢? 答案是,当然可以用,不过我们需要自己的“access denied”页面,这样通过定制我们自己的拒绝页面,就能跟我们的网站风格保持一致。在某些情况下,我们还想为用户提供更多的信息,甚至向管理员发送“未经授权的尝试”类电子邮件。

  这里只是防止下载所有的压缩文件,但是我们实际想要做的是什么?对了,我们要取得从我们站点上文件下载方式的绝对控制权。我们不想让用户直接浏览压缩文件。通过表结构,我们可建立一个项目、用户列表,以及每个用户购买产品的关系表。因此如果我们有一个用户名和一个产品序列号,就可以通过一个简单的数据库查询来判断这个用户是否购买了这个产品。同时,我们还想让用户只需单击一个链接就能启动查询并确定是否被允许下载文件。 这些功能的确非常令人向往。

  二、控制下载

  下面,我们开始讲述如何编写一个用于某些文件请求的处理程序,以及该处理程序的安装方法。我们的处理程序的功能很简单,它只是将请求重定向到其他地方而已。ASP.NET还提供了另外一种文件扩展名即ASHX,它无需安装到web.config文件中。我们可以创建一个以这个扩展名结尾的类,来实现IHttpHandler接口,并就直接导航至该类。实际上它与一个页面非常类似,只是它不会使用Web表单和Code-Behind类,所以它是一种更加简洁的方案。

  现在,我们创建一个新的处理程序称为Download.ashx,并让用户浏览到该处理程序的位置,同时在QueryString参数中规定一些信息。下面的URL就是下载链接:

  ~/Downloads/Download.ashx?Product=101

  这个URL表示下载与产品101有关的文件。用户或者链接可以访问上述URL来试图下载一个文件,这时,该处理程序的ProcessRequest方法就会执行。

  利用标准Forms身份验证对用户身份进行验证,这样就能访问在我们站点上下文中的User对象。请记住,HTTP上下文会传递给该处理程序的ProcessRequest方法,所以能访问到所需的内容。对象User允许我们获得使用User.Identity.Name的授权用户的名称,还有,我们还可以使用该方法访问User表中的用户。访问用户的名称,我们要使用User.Identity.IsAuthenticated的值来检查他们是否已经过身份认证,如果没有,将其重定向到“access denied”。此外,我们还要访问请求的产品号码,代码如下所示:

context.Request.QueryString["Product"]

  这样,我们就取得了产品号码和用户名。 有了这两者,我们就可以访问UserProducts表并确定这个用户是否购买过这个产品。此外,这个表还存储有该产品的文件名。

  既然有了用户名和产品号码,并通过它们确定除了用户购买情况。如果该用户没有购买相应的产品,我们就将其重定向到前面的处理程序,并返回一个访问拒绝页面。为简洁起见,我们将其重定向到一个告知他们还没有购买过此产品的页面,并告知如何进行购买。

  如果确定该用户购买了这个产品,可以通过ProductFileName字段了解该用户可以查看哪些文件。这里,我们没有存储完整的路径,只是存储了文件名。如果需要,我们可以从web.config中的设置来获得该文件夹,所以最终获得了完整的文件路径和名称,并授权下载。 下面我们通过称为StartDownload方法来完成此任务:

private void StartDownload(
       HttpContext context,
string downloadFile)
   {
         context.Response.Buffer
= true;
         context.Response.Clear();
         context.Response.AddHeader(
            
"content-disposition",
            
"attachment; filename=" + downloadFile);
         context.Response.ContentType
=
            
"application/zip";
         context.Response.WriteFile(
            
"~/Downloads/Files/" + downloadFile);
   }

  这里的ProcessRequest方法会调用StartDownload方法,完整的Download.ashx代码如下所示:

<%@ WebHandler Language="C#" Class="Download" %>
using System;
using System.Web;
public class Download : IHttpHandler
{
  
public void ProcessRequest (HttpContext context)
   {
      
if (context.User.Identity.IsAuthenticated)
      {
        
if(context.Request.QueryString["Product"] != null
            
&& context.Request.QueryString["Product"] != "")
         {
            
int productID = Convert.ToInt16(
               context.Request.QueryString[
"Product"]);
            
string userName = context.User.Identity.Name;
            UserProduct product
= UserProductFactory.GetProductByUser(
               userName, productID);
            
if(product != null)
               StartDownload(product.FileName);
            
else
               context.Response.Redirect(
                  
"~/Downloads/Files/AccessDenied.aspx");
         }
      }
   }

  
public bool IsReusable
   {
      
get
      {
        
return false;
      }
   }
  
private void StartDownload(string downloadFile)
   {
      context.Response.Buffer
= true;
      context.Response.Clear();
      context.Response.AddHeader(
        
"content-disposition",
        
"attachment; filename=" + downloadFile);
      context.Response.ContentType
= "application/zip";
      context.Response.WriteFile(
        
"~/Downloads/Files/" + downloadFile);
   }
}

  这个方法会收到文件的名称以及HttpContext。由此处,我们将清空响应缓冲区,设置一个新的头部,然后设置内容类型。最后,使用WriteFile方法输出该文件,最终用户会收到一个文件保存或打开窗口。

  注意,使用WriteFile会输出ZIP 文件,而是要Response.Redirect则会把用户重定向到访问拒绝页面。使用这种技术的时候,聪明的用户可以绕过安全检查而直接导航至Download.ashx文件。但即使它们设法直接浏览到ZIP 文件,也会被FileDenialHandler处理程序重定向到访问拒绝页面。

  注意这两种类型处理程序的区别,一个是可以放在外部组件中的标准C#(或者VB.NET)类,如果您需要编写可重用的处理程序的时候,这种类型比较理想,因为您可以把它们编译成一个动态链接库,并在不同的站点之间分享。当然,我们需要在web.config文件中对它们进行注册。对于ASHX类型的处理程序,可以像ASPX页面一样添加到站点中。事实上,我们可以使用其他技术来完成类似Download.ASHX的功能,但是ASHX处理程序是更加简洁的解决方案。

  三、小结

  使用ASP.NET保护文件下载的所有方法中,利用HTTP处理程序是最重要的一种。通过与其他技术相结合,我们不仅能够防止非授权用户下载文件,还能在处理用户下载文件试图时获得绝对控制权,希望本文的内容对您能够有所帮助。

系列文章索引:

用ASP.NET实现文件的保护性下载基础篇

 

0
相关文章