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

如何基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 00:15:44