Django中如何阻止未购课用户访问对应课程的静态/媒体文件?
问题描述
我正在开发一个Udemy克隆平台,需要实现以下功能:
- 未购课用户仅可查看课程详情
- 购课用户可查看课程详情、课时及视频
现有方案存在漏洞:通过Course模型的外键字段存储有权限的用户,校验当前登录用户是否在该列表中,但未购课用户若知晓课程视频的直接链接,可通过地址栏直接访问该媒体/静态文件。
现有模型代码
class Course(models.Model): id = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4, editable=False) author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='courses') students = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='courses', blank=True, null=True) ... class Section(models.Model): id = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4, editable=False) course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='sections') ... class Lesson(models.Model): id = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4, editable=False) section = models.ForeignKey(Section, on_delete=models.CASCADE, related_name="lessons") video = models.FileField(upload_to=directory_for_images, blank=True, max_length=500) ...
现有视图代码
def courseConditionalView(request, pk): course = models.Course.objects.get(pk = pk) current_user = request.user course_bought = False if current_user.is_authenticated: course_bought = bool(current_user.courses.filter(pk = pk)) if course_bought: return redirect(reverse('course_details_study', kwargs={'pk': str(pk)})) else: return render(request, 'courses/course_detail.html', {'course': course}) def studyView(request, pk): course = models.Course.objects.get(pk = pk) current_user = request.user course_bought = False if current_user.is_authenticated: course_bought = bool(current_user.courses.filter(pk = pk)) if course_bought: return render(request, 'courses/course_detail_study.html', {'course': course}) else: return redirect(reverse(courseConditionalView, kwargs={'pk': str(pk)}))
请问如何阻止未购课用户访问这些静态/媒体文件?或者是否应采用更优的动态权限方案——在课程创建时动态生成权限,用户购课时为其分配对应权限?
解决方案
一、阻止未购课用户直接访问媒体文件
直接暴露媒体文件URL是核心漏洞,必须通过Django视图层中转访问,禁止Web服务器直接提供文件:
修正模型关联错误
首先修复Course.students字段的类型——当前ForeignKey只能关联单个学生,改为ManyToManyField才符合多学生购课的场景:class Course(models.Model): # ... 其他字段 students = models.ManyToManyField(get_user_model(), related_name='courses', blank=True)编写视频中转视图
创建专门的视图处理视频请求,先校验用户权限再返回文件:from django.http import FileResponse, Http404 from django.shortcuts import get_object_or_404 def serve_lesson_video(request, lesson_id): lesson = get_object_or_404(Lesson, id=lesson_id) course = lesson.section.course # 权限校验:已登录且购买了对应课程 if not request.user.is_authenticated or not request.user.courses.filter(pk=course.id).exists(): raise Http404("无权访问该视频") # 流式返回视频文件,支持大文件播放 response = FileResponse(lesson.video.open('rb'), content_type='video/mp4') response['Content-Disposition'] = f'inline; filename="{lesson.video.name.split("/")[-1]}"' return response配置URL路由
将视频访问指向中转视图,而非直接指向媒体文件:path('lesson/<uuid:lesson_id>/video/', views.serve_lesson_video, name='serve_lesson_video'),模板中生成正确链接
在course_detail_study.html中,替换直接的媒体URL为视图路由:<video src="{% url 'serve_lesson_video' lesson.id %}" controls></video>额外安全加固
- 使用签名URL(如
django-signed-urls)给视频链接添加过期时间,防止用户长期分享; - 添加访问频率限制,避免批量下载或爬虫攻击。
- 使用签名URL(如
二、权限方案选择:动态权限vs基础关联方案
基础关联方案(推荐)
如果你的平台只需"购课即可访问全部内容"的逻辑,修改后的ManyToManyField关联方案足够用:
- 实现简单,查询性能高;
- 权限校验逻辑清晰,与现有视图代码兼容。
动态权限方案(复杂场景适用)
若需要细粒度权限控制(如部分用户可访问特定章节、讲师可编辑课程等),可采用动态权限:
课程创建时生成专属权限
from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType def create_course_permissions(course): content_type = ContentType.objects.get_for_model(Course) # 创建课程内容访问权限 view_perm, _ = Permission.objects.get_or_create( codename=f'view_course_{course.id}', name=f'查看课程{course.id}的内容', content_type=content_type, ) return view_perm购课时分配权限
def purchase_course(request, course_id): course = get_object_or_404(Course, id=course_id) perm = Permission.objects.get(codename=f'view_course_{course.id}') request.user.user_permissions.add(perm) course.students.add(request.user) # 保持关联逻辑兼容 return redirect('studyView', pk=course_id)视图中校验权限
if not request.user.has_perm(f'courses.view_course_{course.id}'): raise Http404
方案对比
- 基础关联方案:开发成本低,适合绝大多数Udemy类平台需求;
- 动态权限方案:扩展性强,但实现和维护成本更高,适合复杂权限场景。
内容的提问来源于stack exchange,提问作者iljuhenson
相关产品推荐
相关产品推荐

