如何基于Django与JWT搭建仅授权用户可访问的Web文件/媒体服务器
Django + JWT 实现授权访问私有媒体文件
要实现仅授权用户访问特定媒体文件,核心思路是禁止Web服务器直接暴露媒体文件目录,所有媒体请求必须经过Django的JWT认证视图,验证通过后才返回文件。以下是具体实现步骤:
1. 基础配置调整
首先确保媒体文件不被直接静态访问:
1.1 修改settings.py配置
import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # 媒体文件存储路径 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 对外访问的虚拟路径(不要直接指向MEDIA_ROOT) MEDIA_URL = '/protected-media/'
1.2 移除开发环境的静态媒体服务
在urls.py中,删除原本用于开发环境的媒体文件直接服务代码:
# 删掉这两行: # from django.conf.urls.static import static # urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
2. 集成JWT认证
使用django-rest-framework-simplejwt实现JWT授权:
2.1 安装依赖
pip install djangorestframework-simplejwt
2.2 配置REST Framework
在settings.py中添加认证配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } # 可选:配置JWT过期时间等参数 from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), }
2.3 添加JWT路由
在urls.py中加入获取token的路由:
from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, ) urlpatterns = [ # 其他路由 path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), ]
3. 编写授权媒体视图
创建一个视图,先验证JWT token,再返回媒体文件:
3.1 基础版视图(直接返回文件)
from django.http import FileResponse, Http404 from rest_framework.decorators import api_view, authentication_classes, permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework_simplejwt.authentication import JWTAuthentication import os from django.conf import settings @api_view(['GET']) @authentication_classes([JWTAuthentication]) @permission_classes([IsAuthenticated]) def serve_protected_media(request, file_path): # 构造文件完整路径 full_file_path = os.path.join(settings.MEDIA_ROOT, file_path) # 安全检查:防止路径遍历攻击(必须加) if not os.path.abspath(full_file_path).startswith(os.path.abspath(settings.MEDIA_ROOT)): raise Http404("文件不存在") if not os.path.exists(full_file_path) or not os.path.isfile(full_file_path): raise Http404("文件不存在") # 返回文件,自动适配媒体类型 return FileResponse(open(full_file_path, 'rb'))
3.2 优化版视图(用Nginx加速大文件)
如果服务大文件(如.mp4),直接用Django返回效率低,可通过Nginx的X-Accel-Redirect头让Nginx负责文件传输,Django只做认证:
from django.http import HttpResponse, Http404 from rest_framework.decorators import api_view, authentication_classes, permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework_simplejwt.authentication import JWTAuthentication import os from django.conf import settings @api_view(['GET']) @authentication_classes([JWTAuthentication]) @permission_classes([IsAuthenticated]) def serve_protected_media(request, file_path): full_file_path = os.path.join(settings.MEDIA_ROOT, file_path) # 安全检查同上 if not os.path.abspath(full_file_path).startswith(os.path.abspath(settings.MEDIA_ROOT)) or not os.path.isfile(full_file_path): raise Http404("文件不存在") # 告诉Nginx内部读取文件的路径 response = HttpResponse() response['X-Accel-Redirect'] = f'/internal-media/{file_path}' # 清空Content-Type,让Nginx自动识别 response['Content-Type'] = '' return response
对应的Nginx配置示例:
# 内部路径,仅允许Nginx自身访问 location /internal-media/ { internal; alias /path/to/your/project/media/; } # 转发认证请求到Django location /protected-media/ { proxy_pass http://127.0.0.1:8000; # 你的Django服务地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
4. 配置视图路由
在urls.py中添加媒体视图的路由:
from django.urls import path from .views import serve_protected_media urlpatterns = [ # 其他路由 path('protected-media/<path:file_path>/', serve_protected_media, name='protected-media'), ]
5. 扩展权限控制(可选)
如果需要让用户只能访问自己上传的文件,可以在视图中添加额外校验:
假设你有一个MediaFile模型关联用户:
from django.db import models from django.contrib.auth.models import User class MediaFile(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) file = models.FileField(upload_to='user_files/') upload_time = models.DateTimeField(auto_now_add=True)
修改视图:
@api_view(['GET']) @authentication_classes([JWTAuthentication]) @permission_classes([IsAuthenticated]) def serve_protected_media(request, file_path): try: # 检查文件是否属于当前用户 media_obj = MediaFile.objects.get(file=file_path, user=request.user) full_file_path = media_obj.file.path except MediaFile.DoesNotExist: raise Http404("无访问权限") # 安全检查和返回文件...
6. 前端请求示例
前端请求媒体文件时,需在请求头中携带JWT Token:
// 用axios请求示例 const token = '你的JWT Access Token'; axios.get('/protected-media/user_files/video.mp4', { headers: { 'Authorization': `Bearer ${token}` }, responseType: 'blob' }).then(res => { // 创建可播放的URL const videoUrl = URL.createObjectURL(res.data); document.getElementById('video-player').src = videoUrl; });
内容的提问来源于stack exchange,提问作者Istiak Hassan Emon
相关产品推荐
相关产品推荐

