django使用ajax PUT/DELETE方法请求报错解决:“forbidden (CSRF token missing or incorrect.)

'# django使用ajax PUT/DELETE方法请求报错解决:“forbidden (CSRF token missing or incorrect.)”

一、背景与问题

在Django中,CSRF(Cross-Site Request Forgery)保护机制是默认启用的。该机制通过在模板中插入{% csrf_token %}标签生成一个随机token,并在请求头中通过X-CSRFToken字段传递。对于常规的HTML表单提交,Django会自动处理这个过程,但AJAX请求需要开发者手动处理。

当使用AJAX发送PUT或DELETE请求时,常见错误提示为:"forbidden (CSRF token missing or incorrect.)"。这个问题的根本原因在于:Django的CSRF中间件认为当前请求缺少或错误的CSRF token。

二、基本原理

Django的CSRF保护机制通过以下步骤实现:

  1. 服务器生成一个随机token并存储在session中
  2. 在模板中插入<input type="hidden" name="csrfmiddlewaretoken" value="...">字段
  3. 客户端提交表单时,自动携带该token
  4. 服务器验证token的合法性

对于AJAX请求,需要手动实现:

  1. 从模板中获取token
  2. 在AJAX请求头中添加X-CSRFToken字段
  3. 服务器验证该字段的合法性

三、环境准备

确保环境满足以下条件:

  • Django 3.2+(支持CSRF_COOKIE_DOMAIN)
  • Python 3.8+
  • 前端使用JavaScript(如jQuery或Axios)

四、核心实现

1. 获取CSRF Token

在模板中使用{% csrf_token %}标签会生成一个包含token的<input>字段,可以通过JavaScript获取:

<!-- templates/index.html -->
<input type="hidden" id="csrf-token" value="{{ csrf_token }}">
// 获取CSRF Token
const csrfToken = document.getElementById('csrf-token').value;

2. 在AJAX请求中携带token

使用jQuery时,可以这样设置请求头:

$.ajax({
    url: '/api/delete/',
    type: 'DELETE',
    headers: {
        'X-CSRFToken': csrfToken
    },
    success: function(response) {
        console.log('删除成功:', response);
    }
});

使用Axios时需要显式设置:

axios.delete('/api/delete/', {
    headers: {
        'X-CSRFToken': csrfToken
    }
}).then(response => {
    console.log('删除成功:', response.data);
});

3. 跨域请求时的处理

当使用CORS时,需要配置Django的CORS_ALLOWED_ORIGINS,并确保CSRF token能正确传递:

# settings.py
CORS_ALLOWED_ORIGINS = [
    "https://example.com",
    "https://another-example.com"
]

五、完整案例

场景:删除评论功能

后端代码(views.py)

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json

@csrf_exempt  # 临时禁用CSRF保护用于演示
def delete_comment(request):
    if request.method == 'DELETE':
        try:
            data = json.loads(request.body)
            comment_id = data.get('id')
            # 实际业务逻辑:从数据库删除评论
            return JsonResponse({'status': 'success', 'message': f'Comment {comment_id} deleted'})
        except Exception as e:
            return JsonResponse({'status': 'error', 'message': str(e)}, status=400)
    return JsonResponse({'status': 'error', 'message': 'Invalid request'}, status=405)

前端代码(index.html)

<!-- templates/index.html -->
<input type="hidden" id="csrf-token" value="{{ csrf_token }}">

<button id="deleteBtn">Delete Comment</button>

<script>
document.getElementById('deleteBtn').addEventListener('click', function() {
    const csrfToken = document.getElementById('csrf-token').value;
    
    fetch('/api/delete/', {
        method: 'DELETE',
        headers: {
            'X-CSRFToken': csrfToken,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ id: 123 })
    })
    .then(response => {
        if (!response.ok) throw new Error('Network response was not ok');
        return response.json();
    })
    .then(data => {
        console.log('Success:', data);
    })
    .catch(error => {
        console.error('Error:', error);
    });
});
</script>

六、源码解析

Django的CSRF中间件核心代码位于django/middleware/csrf.py,关键逻辑如下:

class CsrfViewMiddleware:
    def process_request(self, request):
        # 处理GET请求,设置模板上下文变量
        if request.method == 'GET':
            request.csrf_processing_done = True
            return
        
        # 处理其他请求
        if request.method in ('POST', 'PUT', 'DELETE'):
            if request.META.get('HTTP_X_CSRFTOKEN'):
                # 验证X-CSRFToken头
                if not csrf_token(request):
                    raise exceptions.PermissionDenied("CSRF token missing or incorrect.")
            else:
                # 验证表单中的csrfmiddlewaretoken
                if not request.POST.get('csrfmiddlewaretoken'):
                    raise exceptions.PermissionDenied("CSRF token missing or incorrect.")

七、进阶使用

1. 自定义CSRF验证

在需要特殊处理的场景下,可以自定义验证逻辑:

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseForbidden

@csrf_exempt
def custom_csrf_view(request):
    if request.method == 'DELETE':
        # 自定义验证逻辑
        if not request.headers.get('X-CSRFToken'):
            return HttpResponseForbidden("CSRF token missing")
        # 验证逻辑...

2. 跨域请求的解决方案

使用Django CORS Headers扩展:

# settings.py
INSTALLED_APPS += ['corsheaders']

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]

CORS_ALLOWED_ORIGINS = [
    "https://example.com",
    "https://another-example.com"
]

八、性能与工程实践

1. 性能优化

  • 缓存CSRF token:对于频繁访问的页面,可以缓存token以减少生成开销
  • 使用Django的内置CSRF验证:相比自定义实现,内置验证经过充分测试
  • 避免不必要的验证:对于GET请求不需要CSRF验证

2. 安全风险

  • CSRF token泄露:若将token暴露给第三方,可能导致XSS攻击
  • 跨域请求漏洞:未正确配置CORS可能导致CSRF攻击
  • 会话固定攻击:如果token生成算法不够随机,可能被预测

九、常见问题与踩坑

1. 常见错误

错误场景原因解决方案
未携带token忘记在AJAX请求中添加X-CSRFToken在请求头中添加 X-CSRFToken: ${csrfToken}
token过期会话超时后未重新获取token在每次请求前重新获取token
跨域请求失败未正确配置CORS使用corsheaders扩展并配置CORS_ALLOWED_ORIGINS
token验证失败前端未正确获取token检查模板中是否包含{% csrf_token %}标签

2. 常见错误示例

错误代码:

fetch('/api/delete/', {
    method: 'DELETE',
    body: JSON.stringify({ id: 123 })
});

错误原因: 未携带CSRF token头

改进代码:

fetch('/api/delete/', {
    method: 'DELETE',
    headers: {
        'X-CSRFToken': csrfToken,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ id: 123 })
});

十、最佳实践

1. 推荐方案

  • 所有AJAX请求都应携带CSRF token
  • 使用Django的内置CSRF保护机制
  • 对于API接口,建议同时使用JWT等其他认证机制
  • 跨域请求时务必配置CORS

2. 不推荐方案

  • 临时禁用CSRF保护(仅用于测试)
  • 将CSRF token暴露给第三方
  • 在非敏感操作中使用CSRF保护

十一、总结

Django的CSRF保护机制是保障Web应用安全的重要防线。在使用AJAX发送PUT/DELETE请求时,需要特别注意CSRF token的获取和传递。本文深入分析了CSRF保护的工作原理,提供了完整的代码示例和解决方案,同时探讨了性能优化、安全风险和常见错误。在实际开发中,应根据具体场景选择合适的保护方案,既要保证安全性,也要兼顾开发效率。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日