如何在Django中限制媒体文件(Media-File)的访问权限?
解决Django私有视频文件的访问权限限制问题
要解决「拿到URL就能直接访问视频」的问题,核心是让视频文件的请求必须经过Django的权限校验,而不是直接通过Web服务器(Nginx/Apache)对外暴露。以下是具体实现步骤:
1. 调整文件存储位置
将视频文件从默认的MEDIA_ROOT(通常配置为可直接访问的目录)移到无法被Web服务器直接访问的私有目录,比如项目根目录下的private_media:
# settings.py import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 原公开媒体配置保留(如果有其他公开文件) MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' # 新增私有媒体配置 PRIVATE_MEDIA_ROOT = os.path.join(BASE_DIR, 'private_media') PRIVATE_MEDIA_URL = '/private-media/'
同时修改你的视频模型,指定upload_to到私有目录下的子路径:
# models.py from django.db import models from django.contrib.auth.models import User class Video(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE) file = models.FileField(upload_to='videos/') # 文件会存在 private_media/videos/ 下 # 其他字段...
2. 编写权限校验的文件服务视图
创建一个视图,负责接收视频请求、验证用户权限,然后返回文件内容。这里提供两种实现方式:
函数视图版本
# views.py from django.http import FileResponse, Http404 from django.contrib.auth.decorators import login_required from .models import Video @login_required def serve_private_video(request, video_id): # 检查视频是否存在 try: video = Video.objects.get(id=video_id) except Video.DoesNotExist: raise Http404("视频不存在") # 验证当前用户是否为所有者 if video.owner != request.user: raise Http404("你没有权限访问该视频") # 返回视频文件,设置正确的Content-Type video_file = open(video.file.path, 'rb') response = FileResponse(video_file) response['Content-Type'] = 'video/mp4' return response
类视图版本(更符合Django通用视图风格)
# views.py from django.views.generic import DetailView from django.contrib.auth.mixins import LoginRequiredMixin from django.http import Http404, FileResponse from .models import Video class PrivateVideoServeView(LoginRequiredMixin, DetailView): model = Video def get(self, request, *args, **kwargs): self.object = self.get_object() # 权限校验 if self.object.owner != request.user: raise Http404("你没有权限访问该视频") # 返回文件 video_file = open(self.object.file.path, 'rb') response = FileResponse(video_file) response['Content-Type'] = 'video/mp4' return response
3. 配置路由映射
将私有媒体的请求指向上面的视图:
# urls.py from django.urls import path from .views import serve_private_video, PrivateVideoServeView urlpatterns = [ # 函数视图路由 path('private-media/video/<int:video_id>/', serve_private_video, name='serve_private_video'), # 类视图路由(二选一) # path('private-media/video/<int:pk>/', PrivateVideoServeView.as_view(), name='serve_private_video'), ]
4. 修改模板中的视频链接
把原来直接指向video.file.url的src,替换为权限视图的URL:
<!-- 模板文件 --> <video controls> <source src="{% url 'serve_private_video' video.id %}" type="video/mp4"> 你的浏览器不支持视频播放 </video>
5. 优化大文件传输(可选)
如果视频文件较大,直接用FileResponse会占用Django进程资源,推荐使用django-sendfile2库让Web服务器直接发送文件(仅权限校验由Django完成):
- 安装库:
pip install django-sendfile2 - 配置settings(以Nginx为例):
# settings.py SENDFILE_BACKEND = 'sendfile.backends.xsendfile' SENDFILE_ROOT = PRIVATE_MEDIA_ROOT SENDFILE_URL = PRIVATE_MEDIA_URL
- 修改视图:
# views.py from sendfile import sendfile @login_required def serve_private_video(request, video_id): try: video = Video.objects.get(id=video_id) except Video.DoesNotExist: raise Http404("视频不存在") if video.owner != request.user: raise Http404("你没有权限访问该视频") return sendfile(request, video.file.path, attachment=False, mimetype='video/mp4')
同时需要在Nginx配置中添加对X-Accel-Redirect的支持,确保私有目录不会被直接访问。
内容的提问来源于stack exchange,提问作者jefferson
相关产品推荐
相关产品推荐

