You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django结合AWS S3实现仅所有者可见的私有媒体文件访问控制咨询

Django结合AWS S3实现私有媒体文件的用户权限控制方案

前提配置:关闭S3桶公共访问权限

首先需要确保S3存储桶没有公共访问权限,这是所有权限控制的基础:

  • 进入AWS S3控制台,找到对应存储桶,打开「权限」选项卡
  • 开启「阻止公共访问」的全部4项配置
  • 删除存储桶策略中所有允许公共访问的规则
  • 配置文件上传默认ACL为private,禁止对象公开访问

核心实现方案:增加Django权限校验代理层

不要直接返回S3原始链接,改为返回Django服务的代理路由地址,由Django先校验用户权限,再返回文件内容或者短时有效的预签名链接。

第一步:新增私有文件代理路由

在对应app的urls.py中添加路由:

from django.urls import path
from .views import private_file_proxy

urlpatterns = [
    path("private-files/<int:file_id>/image/", private_file_proxy, name="private_file_proxy"),
]

第二步:实现代理校验逻辑(两种可选方案)

首先确保你已经安装boto3并在Django配置中添加了AWS密钥、区域、存储桶名等参数。

方案1:校验通过后返回短时S3预签名URL(推荐,性能更好)

该方案不会占用Django服务的带宽,适合大文件场景,预签名URL过期后无法再访问:

import boto3
from django.conf import settings
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.contrib.auth.decorators import login_required
from .models import PrivateFile

s3_client = boto3.client(
    's3',
    aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
    aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
    region_name=settings.AWS_S3_REGION_NAME
)

@login_required
def private_file_proxy(request, file_id):
    # 校验文件是否存在
    try:
        file_obj = PrivateFile.objects.get(id=file_id)
    except PrivateFile.DoesNotExist:
        raise Http404
    # 校验当前用户是否为文件所有者
    if file_obj.owner != request.user:
        return HttpResponse(status=403)
    # 生成有效期为10分钟的预签名URL,有效期可自行调整,越短安全性越高
    presigned_url = s3_client.generate_presigned_url(
        'get_object',
        Params={
            'Bucket': settings.AWS_STORAGE_BUCKET_NAME,
            'Key': file_obj.image.name
        },
        ExpiresIn=600
    )
    return HttpResponseRedirect(presigned_url)

方案2:校验通过后代理返回S3文件内容

该方案不会暴露S3地址,安全性更高,但会占用Django服务的带宽,适合小文件场景:

@login_required
def private_file_proxy(request, file_id):
    try:
        file_obj = PrivateFile.objects.get(id=file_id)
    except PrivateFile.DoesNotExist:
        raise Http404
    if file_obj.owner != request.user:
        return HttpResponse(status=403)
    # 从S3读取文件内容
    s3_response = s3_client.get_object(
        Bucket=settings.AWS_STORAGE_BUCKET_NAME,
        Key=file_obj.image.name
    )
    # 构造响应返回给前端
    response = HttpResponse(
        s3_response['Body'].read(),
        content_type=s3_response['ContentType']
    )
    # 如需强制下载可添加如下配置
    # response['Content-Disposition'] = f'attachment; filename="{file_obj.image.name.split("/")[-1]}"'
    return response

第三步:修改序列化器返回代理地址

不要直接返回image字段的原生S3地址,改为返回自定义的代理路由地址,DRF序列化器示例如下:

from rest_framework import serializers
from .models import PrivateFile
from django.urls import reverse

class PrivateFileSerializer(serializers.ModelSerializer):
    image = serializers.SerializerMethodField()
    class Meta:
        model = PrivateFile
        fields = ['id', 'owner', 'image', 'created']
    
    def get_image(self, obj):
        request = self.context.get('request')
        relative_url = reverse('private_file_proxy', args=[obj.id])
        return request.build_absolute_uri(relative_url)

额外安全优化建议

  • 预签名URL的有效期建议控制在1~15分钟之间,避免链接泄露后被长期滥用
  • 配置S3桶的CORS规则,仅允许你的业务域名访问
  • 对代理接口添加请求频率限制,防止恶意批量爬取文件
  • AWS密钥不要硬编码在代码中,通过环境变量或者保密配置服务管理
  • 生产环境开启Django的CSRF校验,避免跨站请求伪造攻击

内容的提问来源于stack exchange,提问作者Khan Asfi Reza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 12:48:01