CSRF et API
Fait partie de la section API JSON du Journey Django de Coddy. Leçon 6 sur 19.
Un véritable programme qui envoie POST /api/tasks/ reçoit une réponse 403 Forbidden. La protection CSRF de Django exige que chaque requête POST contienne un jeton secret provenant de l’une de vos propres pages, afin qu’un autre site web ne puisse pas faire soumettre vos formulaires par le navigateur d’un visiteur. Un programme ne dispose ni d’une telle page ni d’un tel jeton.
@csrf_exempt, provenant de django.views.decorators.csrf, désactive la vérification pour une vue :
@csrf_exempt
def webhook(request):
...Cela implique une concession : n’importe quel site web peut désormais faire envoyer cette requête POST par le navigateur d’un visiteur. C’est acceptable pour une vue qui ne dépend pas de l’identité de la personne connectée, comme la liste des tâches. Une API qui agit au nom de la personne connectée conserve la protection CSRF activée, et le script de sa page envoie le jeton dans un en-tête X-CSRFToken.
Défi
MoyenPermets aux programmes sans jeton CSRF de créer des tâches. Dans tasks/views.py, exempte uniquement task_list de la vérification, afin que POST /api/tasks/ fonctionne sans jeton.
Exécute la ligne ci-dessous dans l’onglet Shell, où enforce_csrf_checks=True demande au client de test de vérifier les jetons : elle affiche 201 au lieu de 403.
from django.test import Client; print(Client(enforce_csrf_checks=True).post("/api/tasks/", {"title": "Pay rent"}, content_type="application/json").status_code)Essayez vous-même
import json
from django.http import JsonResponse
from django.shortcuts import render
from .models import Task
def task_to_dict(task):
return {
"id": task.id,
"title": task.title,
"done": task.done,
"priority": task.priority,
}
def home(request):
return render(request, "tasks/home.html", {"tasks": Task.objects.all()})
def stats(request):
data = {
"total": Task.objects.count(),
"done": Task.objects.filter(done=True).count(),
}
return JsonResponse(data)
def task_list(request):
if request.method == "POST":
data = json.loads(request.body)
task = Task.objects.create(
title=data["title"],
priority=data.get("priority", "normal"),
)
return JsonResponse(task_to_dict(task), status=201)
tasks = Task.objects.all()
done = request.GET.get("done")
if done == "true":
tasks = tasks.filter(done=True)
elif done == "false":
tasks = tasks.filter(done=False)
return JsonResponse([task_to_dict(task) for task in tasks], safe=False)
def task_detail(request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return JsonResponse({"error": "Task not found"}, status=404)
return JsonResponse(task_to_dict(task))
Toutes les leçons de API JSON
2Lire les requêtes
Filtrer avec une requêteCréer avec POST5Projet : Trier le travail
Projet : Les prioritésProjet : Effacer les tâches terminéesEntraînez-vous par vous-même : Playground Django