web-dev-qa-db-fra.com

403 Erreur interdite lors de la création d'une demande de publication ajax dans la structure Django

J'essaie d'intégrer jQuery dans une application Web que je réalise avec le framework Django. Cependant, j'ai de la difficulté à essayer de faire un simple appel ajax au travail. Mon fichier de modèle contenant le formulaire html et javascript permettant de gérer l'appel ajax se présente comme suit:

<script type="text/javascript">
$(document).ready(function() {
$( "#target" ).submit(function() {
console.log('Form was submitted');
$.ajax({
        type: "POST",
        url: "/hello/",  // or just url: "/my-url/path/"
        data: {
            query: $( "#query" ).val()   
        },
        success: function(data) {
            console.log(data);
        }
    });
return false;
  });   
  })
</script>
<form id="target" action="." method="post">{% csrf_token %}
 <input id= "query" type="text" value="Hello there">
 <input type="submit" value="Search Recent Tweets">
</form>

Mon views.py censé gérer l'appel ajax ressemble à ceci:

 from Django.core.context_processors import csrf
 from Django.shortcuts import render_to_response
 from Django.template.loader import get_template
 from Django.template import Context,RequestContext
 from Django.views.decorators.csrf import ensure_csrf_cookie
 from Django.http import HttpResponse

 # access resource
 def hello(request):
  c = {}
  c.update(csrf(request))
  if request.is_ajax():
        t = get_template('template.html')
        #html = t.render(Context({'result': 'hello world'}))
        con = RequestContext(request, {'result': 'hello world'})
        return render_to_response('template.html', c, con)
  else:
        return HttpResponse('Not working!') 

J'ai essayé de suivre la documentation officielle sur Protection contre la falsification des requêtes intersites et j'ai également examiné plusieurs questions de stackoverflow traitant d'un problème similaire. J'ai inclus le {% csrf_token %} dans mon fichier de modèle html mais il ne semble toujours pas fonctionner. Je reçois une erreur dans la console suggérant l'échec de l'appel ajax:

POST http://127.0.0.1:8000/hello/ 403 (FORBIDDEN)   

Comment puis-je transmettre la variable result avec ma réponse http et obtenir le bon fonctionnement de l'appel ajax? Toute aide est très appréciée.

Edit-1

Je n’étais pas censé passer le jeton csrf avec ma demande de publication. SO conformément à la documentation, j'ai ajouté le code suivant à mon modèle javascript:

function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
    var cookies = document.cookie.split(';');
    for (var i = 0; i < cookies.length; i++) {
        var cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
            break;
        }
    }
}
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');
console.log(csrftoken);

//Ajax call
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    crossDomain: false, // obviates need for sameOrigin test
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

Lorsque j'actualise la page HTML du modèle dans le navigateur, je reçois null dans la console, ce qui suggère que le cookie n'est pas défini ou défini. Qu'est-ce que je rate?

30
Annihilator8080

Parce que vous n'avez pas posté le csrfmiddlewaretoken, Django vous interdit donc… .. ce document peut vous aider.

40
Yohn

Pour les gars paresseux:

Premier cookie téléchargé: http://plugins.jquery.com/cookie/

Ajoutez-le à votre html:

<script src="{% static 'designer/js/jquery.cookie.js' %}"></script>

Vous pouvez maintenant créer une requête POST opérationnelle:

var csrftoken = $.cookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

$.ajax(save_url, {
    type : 'POST',
    contentType : 'application/json',
    data : JSON.stringify(canvas),
    success: function () {
        alert("Saved!");
    }

})
21
fivef

Je trouve toutes les réponses précédentes sur place, mais mettons les choses en contexte.

La réponse 403 interdite provient du middleware CSRF (voir Protection contre la falsification de requêtes sur site ): 

Par défaut, une réponse «403 interdite» est envoyée à l’utilisateur si une requête entrante échoue aux vérifications effectuées par CsrfViewMiddleware.

De nombreuses options sont disponibles. Je recommanderais de suivre la réponse de @fivef afin que jQuery ajoute l'en-tête X-CSRFToken avant chaque demande AJAX avec $.ajaxSetup.

Cette réponse nécessite le cookie jQuery plugin. Si cela n'est pas souhaitable, une autre possibilité consiste à ajouter:

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

MAIS: si le paramètre CSRF_COOKIE_HTTPONLY est défini sur True, ce qui se produit souvent lorsque le intergiciel de sécurité le recommande, le cookie n’est pas présent, même si @ensure_csrf_cookie() est utilisé. Dans ce cas, {% csrf_token %} doit être fourni dans chaque formulaire, ce qui génère une sortie telle que <input name="csrfmiddlewaretoken" value="cr6O9...FUXf6" type="hidden">. Donc, la variable csrfToken serait simplement obtenue avec:

var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();

Encore une fois, $.ajaxSetup serait requis, bien sûr.

Les autres options disponibles mais non recommandées consistent à désactiver le middleware ou la protection csrf du formulaire spécifique avec @csrf_exempt().

5
Wtower

Pour définir le cookie, utilisez le ensure_csrf_cookie décorateur à votre vue:

from Django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def hello(request):
    code_here()
2
kelvinss

Assurez-vous de ne pas mettre en cache la page/vue sur laquelle votre formulaire apparaît. Cela pourrait être la mise en cache de votre CSRF_TOKEN. Qui m'est arrivé!

1
teewuane

La solution la plus rapide si vous n’intégrez pas js dans votre modèle est la suivante: 

Placez <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> devant votre référence au fichier script.js dans votre modèle, puis ajoutez csrfmiddlewaretoken dans votre dictionnaire data:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

Si vous intégrez votre js au modèle, c’est aussi simple que: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

1
Marek Židek

Essayez d'inclure ce décorateur sur votre code d'expédition

from Django.utils.decorators import method_decorator
from Django.views.decorators.csrf import csrf_exempt     
@method_decorator(csrf_exempt, name='dispatch')
def dispatch(self, request, *args, **kwargs):
     return super(LessonUploadWorkView,self).dispatch(request,*args,**kwargs)
1
mullerivan

Avec SSL/https et avec CSRF_COOKIE_HTTPONLY = False, csrftoken ne figure toujours pas dans le cookie, ni avec la fonction getCookie (nom) proposée dans Django Doc ni avec le jquery. cookie.js proposé par fivef .

Wtower summary est parfait et je pensais que cela fonctionnerait après la suppression de CSRF_COOKIE_HTTPONLY de settings.py mais ce n’est pas dans https!

Pourquoi csrftoken n'est pas visible dans document.cookie ???

Au lieu d'obtenir 

"Django_language = fr; csrftoken = rDrGI5cp98MnooPIsygWIF76vuYTkDIt"

Je reçois seulement

"Django_language = fr"

POURQUOI? Comme SSL/https supprime X-CSRFToken des en-têtes Je pensais que c'était dû aux paramètres d'en-tête de proxy de Nginx, mais apparemment pas ... Une idée?

Contrairement à Django doc Notes , il semble impossible de travailler avec csrf_token dans des cookies avec https. La seule façon de passer csrftoken est de passer par le DOM en utilisant {% csrf_token%} en HTML et de l'obtenir dans jQuery en utilisant

var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();

Il est alors possible de le transmettre à ajax soit par entête ( xhr.setRequestHeader ), soit par params .

0
openHBP