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

Django中如何限制未认证用户访问媒体文件的URL?

Django私有媒体文件访问权限控制解决方案

你的问题根源是默认情况下Django的媒体文件是直接由Web服务器静态分发,不会经过Django的权限校验逻辑,所以只要拿到URL就能直接访问,以下是可落地的解决方案:

方案1:Django视图代理返回文件(小文件场景适用)

适合文件体积小、访问量低的场景,所有文件请求都走Django视图做权限校验:

  • 不要直接暴露媒体文件的真实存储路径,将MEDIA_URL设置为需要权限校验的路由前缀,比如/private-media/
  • 编写专用的媒体文件服务视图,在视图内完成用户身份、文件所有权校验,校验通过后才返回文件内容,示例代码:
from django.http import HttpResponse, Http404
from django.contrib.auth.decorators import login_required
from .models import UserFile
import os
from django.conf import settings

@login_required
def serve_private_media(request, file_path):
    # 校验当前用户是否是文件所有者
    user_file = UserFile.objects.filter(file=file_path, owner=request.user).first()
    if not user_file:
        raise Http404
    # 拼接真实文件存储路径
    full_path = os.path.join(settings.MEDIA_ROOT, file_path)
    # 读取文件返回响应
    with open(full_path, 'rb') as f:
        response = HttpResponse(f.read(), content_type="application/octet-stream")
        response['Content-Disposition'] = f'inline; filename="{os.path.basename(full_path)}"'
        return response
  • 配置项目路由,将/private-media/<path:file_path>路径绑定到上述视图即可。

方案2:X-Accel-Redirect/X-Sendfile 代理(生产环境大文件适用)

适合生产环境大文件场景,由Django完成权限校验,Web服务器负责返回文件内容,性能和静态分发一致:

  • 首先配置Web服务器禁止外部直接访问真实媒体路径,以Nginx为例,配置示例:
location /real-media/ {
    internal; # 仅允许内部请求访问,外部直接访问返回404
    alias /path/to/your/media/root/; # 真实媒体文件的本地存储路径
}
  • 修改Django视图,权限校验通过后返回X-Accel-Redirect响应头,告知Nginx返回对应文件,示例代码:
from django.http import HttpResponse, Http404
from django.contrib.auth.decorators import login_required
from .models import UserFile
import os

@login_required
def serve_private_media(request, file_path):
    user_file = UserFile.objects.filter(file=file_path, owner=request.user).first()
    if not user_file:
        raise Http404
    response = HttpResponse()
    # 告知Nginx返回对应内部路径的文件
    response['X-Accel-Redirect'] = f'/real-media/{file_path}'
    response['Content-Disposition'] = f'inline; filename="{os.path.basename(file_path)}"'
    del response['Content-Type'] # 让Nginx自动判断文件Content-Type
    return response

方案3:签名URL+过期时间(临时访问场景适用)

如果需要给用户提供临时访问链接,避免历史URL长期有效,可以用带签名和过期时间的URL:

  • 生成URL时,拼接用户ID、文件ID、过期时间戳,用密钥生成签名,最终URL格式类似/private-media/xxx.pdf?user=123&expire=1717234567&sign=abc123def
  • 用户访问时先校验签名是否合法、是否已过期,再校验用户权限,就算用户拿到历史URL,过期后也会自动失效,也无法篡改参数伪造合法链接
  • 可以直接用Django内置的django.core.signing模块实现,示例代码:
from django.core.signing import Signer, BadSignature
from datetime import datetime

# 生成签名URL
signer = Signer()
file_path = "xxx.pdf"
expire_ts = int(datetime.now().timestamp() + 3600) # 1小时后过期
signed_str = signer.sign(f"{file_path}:{request.user.id}:{expire_ts}")
access_url = f"/private-media/{file_path}?sign={signed_str}"

# 校验签名
def check_access(sign_str, file_path, current_user_id):
    try:
        original = signer.unsign(sign_str)
        path, uid, exp = original.split(":")
        return path == file_path and int(uid) == current_user_id and int(exp) > datetime.now().timestamp()
    except BadSignature:
        return False

注意事项

  • 生产环境禁止直接使用Django默认的django.views.static.serve分发媒体文件,也不要将MEDIA_ROOT下的路径直接暴露给外部访问
  • 所有媒体文件的访问请求必须先经过Django的权限校验逻辑,再返回文件内容或者内部重定向指令

内容的提问来源于stack exchange,提问作者Abdul Halim Chowdhury

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 21:15:05