创建临时下载链接

6

我使用ASP.NET
我需要为用户提供从服务器下载文件的临时链接。
这应该是一个临时链接(页面),在短时间内可用(例如12小时)。我如何生成此链接(或带有链接的临时网页)?

5个回答

11

这里是一个相当完整的示例。

首先,一个函数用来创建一个带有秘密盐和过期时间的短十六进制字符串:

public static string MakeExpiryHash(DateTime expiry)
{
    const string salt = "some random bytes";
    byte[] bytes = Encoding.UTF8.GetBytes(salt + expiry.ToString("s"));
    using (var sha = System.Security.Cryptography.SHA1.Create())
        return string.Concat(sha.ComputeHash(bytes).Select(b => b.ToString("x2"))).Substring(8);
}

以下是生成一周过期链接的代码片段:

DateTime expires = DateTime.Now + TimeSpan.FromDays(7);
string hash = MakeExpiryHash(expires);
string link = string.Format("http://myhost/Download?exp={0}&k={1}", expires.ToString("s"), hash);

如果提供了有效链接,最终将下载页面发送给文件:

DateTime expires = DateTime.Parse(Request.Params["exp"]);
string hash = MakeExpiryHash(expires);
if (Request.Params["k"] == hash)
{
    if (expires < DateTime.UtcNow)
    {
        // Link has expired
    }
    else
    {
        string filename = "<Path to file>";
        FileInfo fi = new FileInfo(Server.MapPath(filename));
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
        Response.AddHeader("Content-Length", fi.Length.ToString());
        Response.WriteFile(fi.FullName);
        Response.Flush();
    }
}
else
{
    // Invalid link
}

你应该一定要加上异常处理来捕获错误的请求。


发送带有秒的日期时间值可能会导致从客户端检测到潜在危险的 Request.Path 值(:)。 - franko_camron
Response.WriteFile是一种好的方式吗?我认为它会在服务器上下载文件,然后再下载到用户计算机上。(如果我的文件存储在其他服务器上) - A Programmer

9
http://example.com/download/document.pdf?token=<token>

这里的关键部分是<token>。如果您不想涉及数据库,可以加密链接创建时间,将其转换为URL安全的Base64表示,并提供给用户该URL。当请求该URL时,解密token并将其中存储的日期与当前日期和时间进行比较。

或者,您可以拥有一个单独的DownloadTokens表,将这些token(可以是GUID)映射到过期日期。


1
我非常喜欢这个想法。我的建议是使用一个控制器,并加密要下载的文件以及时间。否则,您必须使用httpmodule或其他方法来防止用户仅从URL中删除令牌。 - Kevin Buchan
1
@Kevin 为什么要加密文档?确实,你需要一个单独的 IHttpHandler,但这并不是什么大问题。 - Anton Gogolev
1
实际上,我想加密文档的名称。如果您想加密文档本身,可以使用SSL。 我建议加密文档名称的原因是因为我在一家律师事务所工作,许多文档都是机密的,甚至名称也是机密的。通过在URL中加密文档,您使得未经授权的人更难获取文档。 - Kevin Buchan

3

在查询字符串中给URL添加一个时间戳:

page.aspx?time=2011-06-22T22:12

检查时间戳是否与当前时间匹配。

为了防止用户自己更改时间戳,还需要计算一些秘密哈希值,并将其追加到查询字符串中:

page.aspx?time=2011-06-22T22:12&timehash=4503285032

如果要使用哈希,你可以做一些像日期时间中所有字段求和再对某个质数取模,或者是对时间的字符串表示进行SHA1哈希等操作。这样用户就无法在不知道正确哈希值的情况下更改时间戳。在你的page.aspx页面中,你可以将给定的哈希值与时间戳的哈希值进行比较。


1

有很多种方法可以实现。

我曾经为一个项目做过的一种方式是生成唯一的密钥,并使用动态下载脚本来流式传输文件。当文件请求被发出时,密钥会被生成并存储在数据库中,同时记录创建时间和所请求的文件。您可以构建一个链接到下载脚本,并传入密钥。从那里开始,很容易跟踪到期时间。


1

llya

我假设您不需要任何身份验证,安全也不是问题 - 也就是说,如果有人获得了URL,他们也可以下载文件。

个人建议创建一个HttpHandler,然后创建一些唯一的字符串,可以将其附加到URL中。

然后在ProcessRequest void中测试编码参数是否仍然可行(在指定的时间范围内),如果是,则使用BinaryWrite呈现文件;如果不是,则可以使用Response.Write(“已过期”)呈现一些HTML。

类似于:

public class TimeHandler : IHttpHandler, IRequiresSessionState
{
    public void ProcessRequest ( HttpContext context )
    {
    
        if( this.my_check_has_expired( this.Context.Request.Params["my_token"] ) )
        {
            // Has Expired 

            context.Response.Write( "URL Has Expired" );
            return;
        }

        // Render the File
        Stream stream = new FileStream( File_Name , FileMode.Open );

        /* read the bytes from the file */
        byte[] aBytes = new byte[(int)oStream.Length];
        stream.Read( aBytes, 0, (int)oStream.Length );
        stream.Close( ); 

        // Set Headers
        context.Response.AddHeader( "Content-Length", aBytes.Length.ToString( ) );
        // ContentType needs to be set also you can force Save As if you require

        // Send the buffer
        context.Response.BinaryWrite( aBytes );                            

    }
}

接下来,您需要在IIS中设置处理程序,但这取决于您使用的版本有所不同。


严格来说,您不需要IRequireSession State,这取决于您是否还进行了某种形式的身份验证或会话跟踪。 - hokapoka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接