如何生成Django一次性下载链接

时间:2019-01-28 13:57:03

标签: python django security hyperlink download

我想保护我项目中的可下载文件,但不知道如何实现。 每次调用post_detail视图时,都会生成一个有效期为60分钟的新下载链接,该链接也只能是可访问的。

models.py

const string sqlErrorMessage = "MyCustomMessage";
var sqlException = FormatterServices.GetUninitializedObject(typeof(SqlException)) as SqlException;
var messageField = typeof(SqlException).GetField("_message", BindingFlags.NonPublic | BindingFlags.Instance);
messageField.SetValue(sqlException, sqlErrorMessage);

views.py

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(verbose_name="Post Title", max_length=25)
    content = models.TextField(verbose_name="Post Content", max_length=5000)
    tag = models.CharField(verbose_name="Tags/Meta - (sep. by comma)", max_length=50, blank=True)
    category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE, null=True)
    postattachment = fields.FileField(
        verbose_name="Post Attachment",
        blank=True,
        null=True,
        upload_to=get_file_path_user_uploads,
        validators=[file_extension_postattachment, file_size_postattachment]

    published_date = models.DateField(auto_now_add=True, null=True)


    def publish(self):
        self.published_date = timezone.now()
        self.save()

    class Meta:
        verbose_name = "Post"
        verbose_name_plural = "Post(s)"
        ordering = ['-title']

    def __str__(self):
        return self.title

如果smb。也许有一些练习示例,将非常有帮助。

预先感谢

3 个答案:

答案 0 :(得分:0)

我不是Django专家,但是我认为这是您无法完全在Django中实现的。在Django中执行了请求后,也就是说,您已经成功为用户生成了下载链接,您无法在60分钟后返回并使该链接无效。不是纯粹的Django(请修复我!)。

另一个阻塞原因是Django根本不旨在提供文件。文件(静态和媒体)旨在由Django前面的网络服务器提供服务(apache / nginx / etc ...)。例如,可以通过以下链接访问由Django存储的文件:https://my-django-app.venom.com/media/my_file.jpg

这里的问题是,文件的位置很容易猜到。为了更容易猜测,您应该将其放在带有长随机字符串的文件夹中,如下所示: https://my-django-app.venom.com/media/b926yqagf6qrzpyew7h3kghtejayxp/my_file.jpg

要实现这种功能,我看到了两种解决方法(可能还有很多其他选择,但我马上想到了这两种):

生成并删除随机路径

要使60分钟后路径无效,您必须按顺序对每个文件请求执行以下操作:

  1. 生成随机字符串
  2. 使用生成的随机字符串作为名称创建文件夹
  3. 将要提供的文件复制到该文件夹​​中(文件的原始版本应存储在MEDIA文件夹之外,以提高安全性
  4. 使用生成链接向用户展示
  5. 在某个位置注册此URL并为其设置一个到期日期,例如,从生成起60分钟(我将为此创建一个简单的模型并将其存储在SQL中)
  6. 每分钟在存储的URL上运行一项作业,如果该作业已过期,请从文件系统中删除其文件夹

要实现第六步,您必须使用Celery扩展Django应用程序。使用Celery,您可以轻松安排工作(Google为celery-beat)。该作业将每分钟执行一次(或根据您的喜好执行),查询当前时间之后存储的URL,并从文件系统上MEDIA文件夹中删除随机字符串文件夹及其内容。芹菜非常简单,在线上有很多很好的例子。

使用对象存储服务器管理Django外部的过期链接

通过将用户上载的内容存储在对象存储(例如minio)中,在基础结构级别上管理到期链接非常容易。 Minio非常类似于Amazon S3,但是是开源的,可以在您自己的位置托管。 Minio可以为存储的文件生成链接,您可以在1分钟到1周之间设置到期时间。 在Django中,您所要做的就是从minio请求链接并指定到期时间。其余的由minio管理。

要实现此方法,您必须扩展Django的File Storage API(https://docs.djangoproject.com/en/2.1/ref/files/storage/)并利用为Django编写的微型客户端之一。我建议使用django-minio-storage(https://github.com/py-pa/django-minio-storage)。

如果采用这种方法,则可以使Django与用户上传的内容完全脱钩,并不再依赖网络服务器提供Django的MEDIA文件夹中的文件。

祝你好运!

更新:

这是我在Django 2.x中基于软件包django-minio-storage的minio存储后端的实现。我是在相对较长的时间之前创建的,它可能充满了不良做法和骇人听闻的解决方案。随时将其用作参考作品:https://gist.github.com/theriverman/db9025a22e0f3c40810bed5f12a139d8

答案 1 :(得分:0)

您可以检出此pypi软件包django-onetimelink。它可以创建一个包含所有上传文件的一次性链接的站点。而且它可以用来从上传的文件生成一次性链接。

答案 2 :(得分:0)

如果您需要一次性链接,

  1. 使用pk(能够传递值)创建django网址。
  2. 创建一组列表哈希并将其存储在数据库中。
  3. 编写一个包含pk的视图,并使用可用的哈希列表进行检查,如果通过检查,则向用户提供了可下载文件,延迟celery任务删除文件(一段时间后),然后从列表中删除哈希避免将来使用。否则,如果检查失败,则重定向URL错误页面。
  4. 应该为用户提供未使用的哈希,该哈希只能使用一次。

希望你有主意。